Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Perl and Haskell: Can the Twain Ever Meet? (tl;dr: yes)

This talk is about two Perl modules (Call:Haskell and Functional::Types) I developed to call Haskell functions as transparently as possible.
In general, the only way to guarantee the correctness of the types of the function arguments in Haskell is to ensure they are well-typed in Perl. So I ended up writing a Haskell-inspired type system for Perl. In this talk I will first discuss the approach I took to call Haskell from Perl, and then the reasons why a type system is needed, and the actual type system I developed. The type system is based on "prototypes", functions that create type descriptors, and a small API of functions to create type constructors and manipulate the types. The system is type checked at run time and supports sum types, product types, function types and polymorphism. The approach is not Perl-specific and suitable for other dynamic languages.


  • Als Erste(r) kommentieren

Perl and Haskell: Can the Twain Ever Meet? (tl;dr: yes)

  1. 1. Perl and Haskell: Can the Twain Ever Meet? Wim Vanderbauwhede School of Computing Science, University of Glasgow, UK (tl;dr: yes)
  2. 2. Outline Perl Haskell Calling Haskell from Perl C Haskell FFI Perl Inline::C Serialisation What About the Types? The Final Picture The Need for Proper Typing A Modest Proposal Implementation Internals Example Conclusions
  3. 3. Perl ... Notorious $_=’while(read+STDIN,$_,2048){$a=29;$b=73; $c=142;$t=255;@t=map{$_%16or$t^=$c^=($m= (11,10,116,100,11,122,20,100)[$_/16%8])&110; $t^=(72,@z=(64,72,$a^=12*($_%16 -2?0:$m&17)), $b^=$_%64?12:0,@z)[$_%8]}(16..271); if((@a=unx"C*",$_)[20]&48){$h =5;$_=unxb24, join"",@b=map{xB8,unxb8,chr($_^$a[--$h+84])} @ARGV;s/...$/1$&/;$ d=unxV,xb25,$_;$e=256| (ord$b[4])<‌<9|ord$b[3];$d=$d>‌>8^($f=$t& ($d>‌>12^$d>‌>4^$d^$d/8))<‌<17,$e=$e>‌>8^($t &($g=($q=$e>‌>14&7^$e)^$q*8^$q<‌<6))<‌<9,$_= $t[$_]^(($h>‌>=8)+=$f+(~$g&$t))for @a[128..$#a]}print+x"C*",@a}’;s/x/pack+/g; eval; But I like Perl
  4. 4. Haskell ... Most empathically not dynamically typed ... Makes easy things hard ... But I like Haskell
  5. 5. Haskell ... a reputation of being esoteric Monas Hieroglyphica, (“The Hieroglyphic Monad”), John Dee, Antwerp 1564
  6. 6. Calling Haskell from Perl Why?
  7. 7. Calling Haskell from Perl Should be as simple as:
  8. 8. Calling Haskell from Perl C, the lingua franca Haskell FFI to call Haskell from C Perl Inline::C to call C from Perl Serialisation
  9. 9. Code Generation The call to “use Call::Haskell” triggers extensive code generation: Haskell FFI and serialisation wrappers Corresponding C wrappers and create a C library Inline::C wrapper to call the functions in the C library Perl serialisation wrapper Compile only when Haskell source code has changed, otherwise cache
  10. 10. Serialisation JSON? YAML? MessagePack? No: Read and Show Because serialisation needs to be type-aware Serialisation of Perl values is done via a custom showH function uses Haskell type information via Data.Typeable, serialised using show, converted to a Perl datastructure in Haskell and deserialised using eval. De-serialisation of Perl serialised values in Haskell is done via read Serialisation of Haskell values is done via show string generated by show is converted into Perl code in Haskell De-serialisation of Haskell result is done via a custom readH function uses eval and Perl’s autload feature
  11. 11. Linking FFI uses ghc as linker Perl requires gcc linker ghc does not compile dynamic libraries by default Lots of fun
  12. 12. The Final Picture 1. Perl script calls a 2. Perl serialisation wrapper which calls a 3. Perl Inline::C wrapper which calls a 4. C FFI wrapper which calls a 5. Haskell FFI wrapper which calls a 6. Haskell serialisation wrapper which calls 7. the original Haskell function All wrappers are generated and built automatically on loading Call::Haskell
  13. 13. What About the Types? The outlined approach relies on Data.Typeable, Read and Show. Data.Typeable does not provide information on type constructors for algebraic types. So this approach only works for “primitive” types and containers holding primitive types. This is already quite a lot, but it’s not good enough. We could add other types (e.g. Data.Map and Maybe) ad hoc However ...
  14. 14. The Need for Proper Typing There is a fundamental issue with variant types. Given following Haskell type: data Qual a = Groot a | Klein a It is impossible to generate the correctly typed value for Haskell unless the value in Perl is also typed with the same information! So we need a type system for Perl...
  15. 15. A Modest Proposal I propose to create a type system to type datastructures and function signatures in a dynamically typed language (Perl), without changing anything to the language itself but nevertheless with an acceptable and usable syntax. The types must be compatible with Haskell’s types so that it is possible to call Haskell functions with well-typed arguments and return values. show and read the typed values in Haskell fashion.
  16. 16. Basic Functionality Declare the type of a typed variable: tv :: T Construct a typed value from other typed and/or untyped values: tv = TC v Bind a typed value to a typed variable: tv = tv Convert a typed value into an untyped value: v u = tv
  17. 17. A Simple Type System The type system provides: The primitive scalar types Int, Float, String, Bool. type TI = Int type TF = Float The container types Tuple, Array and Map: type TT = (T1, T2, ..., TN ) type TA = [T1] type TM = {Tk , Tv } A mechanism to declare and construct sum and product types: data TS = T1 | T2 ... TN data TP = TCP T1 ... TN
  18. 18. Implementation To implement this type system in Perl, we use functions for the type constructors, a small API of type naming, binding and construction functions. prototypes to construct the actual types. Prototypes are functions that return a type descriptor. lightweight objects to represent the typed values and the type descriptors returned by the prototypes. The implementation relies on two language features: a function can query the name of its callers (caller in Perl) data structures can be transformed into lightweight objects (bless in Perl)
  19. 19. newtype and typename To create type constructors: TC = newtype T (Prototype T1...TN ) If the type T is polymorphic, the type name can take parameters: TC = newtype (T a b) (Prototype a b) Also used for container types TC = newtype T (Prototype T1...TN ) And to create an alias for an existing primitive type T = newtype Tprim To declare the type name T T = typename
  20. 20. Prototypes Algebraic datatypes: Record: For product types, with optional support for named fields Variant: For sum types Container types: Tuple: For tuples Array: For lists Map: For dictionaries Functions: Function To construct primitive types: Scalar
  21. 21. type and bind To declare a typed variable type tv T If the type is a polymorphic type, we can provide the type argument: type tv (T T1) To bind a typed variable to a value, we use bind, which corresponds to “=” in Haskell. bind tv tv If the type of a typed variable is primitive, bind can take an untyped value as the argument: bind tv v
  22. 22. untype, show and read To return an untyped value from a typed value v = untype tv Finally, read and show work as in Haskell
  23. 23. Internals: Type Names and Prototypes Type Names The typename function returns a tuple of the name of the type and a list of the names parameters, if any. For example, given Couldbe = typename then the call Couldbe a b will return the tuple (Couldbe, [a,b]). Prototypes The prototypes take type name functions as arguments, and return a type descriptor, implemented as a lightweight object. Prototypes can only be called as argument to newtype, which in its turn can only be called in the body of a type constructor function. For example: IntVar = typename MkIntVar = newtype IntVar (Record String Int) The call to Record will return and object of type Record with as attributes the type constructor name and a list of arguments to the constructor, i.e. typenames
  24. 24. Internals: Typed Value Objects and Type Construction Typed Value Object An object with attributes Type and Value. The Type attribute contains the information returned by the typename or prototype calls, the Value attribute is populated by a call to newtype or bind. Type Construction The call to newtype typechecks the arguments against the type information returned by the prototype, and returns a typed value object, with the Type field provided by the prototype.
  25. 25. Internals: Typing and Binding The call to type returns a typed value object with empty Value attribute, to be filled by a call to bind. The call to bind takes a value, typechecks it and stores it in the Value attribute of the typed value. If the type is a primitive type or a container type holding primitive types, then the value can be untyped and is stored as-is in the Value attribute. In that case, the only checking that is performed is on the type of the container. Otherwise, the value must be typed and will be typechecked.
  26. 26. Internals: Typing and Binding For example: type iv IntVar bind iv (MkIntVar “55” 7188 ) The call to MkIntVar will return a typed value object with as Type attribute a Record object with typename IntVar. The typed value iv will contain as Type attribute a tuple with as first argument the typename IntVar. Type checking is simply a comparison of the typenames. In a simpler example: type iv’ Int bind iv’ (Int 42) the call to (Int 42) would return a typed value object, and the bind call would typecheck the typename from the Type attribute with the typename from iv’
  27. 27. Function Types We often want to create typed functions from untyped functions. type f Int -> Int -> Int bind f (x y ->x+y) But an untyped function can still take and return typed values: type f (Int,Int) -> Maybe Int bind f ((x,y) -> if (y/=0) then Just x/y else Nothing) We allow both bare and typed values for the argument, and return typed values from the function. The call to bind creates a new function which takes typed arguments, typechecks, untypes them if required, passes them to the original function, and types the return value if required.
  28. 28. Polymorphic Binary Tree Example Polymorphic Binary Tree Tree = typename Branch = newtype (Tree a) (Variant Tree a Tree) Leaf = newtype (Tree a) Variant type tree Tree(Int) bind tree (Branch (Branch Leaf 41 Leaf) 42 Leaf) Actual Perl code:
  29. 29. Typed Function Call Example use Call::Haskell import => 'VarDeclParser( parse_vardecl )'; use Types; use VarDecl; my $tstr = String("integer :: v"); type my $fp = String => VarDecl; bind $fp, &parse_vardecl; my $tres = $fp−>($tstr); say show $tres; my $res = untype $tres;
  30. 30. Summary Want proper typechecking in Perl? https://github.com/wimvanderbauwhede/Perl-Functional-Types Want to call Haskell from Perl? https://github.com/wimvanderbauwhede/Perl-Call-Haskell
  31. 31. Thank you! Questions?