33
Perl and Haskell: Can the Twain Ever Meet? Wim Vanderbauwhede School of Computing Science, University of Glasgow, UK (tl;dr: yes)

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

Embed Size (px)

Citation preview

Perl and Haskell: Can the Twain Ever Meet?Wim VanderbauwhedeSchool of Computing Science, University of Glasgow, UK

(tl;dr: yes)

Outline

I PerlI HaskellI Calling Haskell from Perl

I CI Haskell FFII Perl Inline::CI Serialisation

I What About the Types?I The Final PictureI The Need for Proper Typing

I A Modest ProposalI ImplementationI InternalsI Example

I Conclusions

Perl ...

I 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;

I But I like Perl

Haskell ...

I Most empathically notdynamically typed ...

I Makes easy things hard ...I But I like Haskell

Haskell ...

a reputation of being esoteric

Monas Hieroglyphica,(“The Hieroglyphic Monad”),

John Dee, Antwerp 1564

Calling Haskell from Perl

I Why?

Calling Haskell from Perl

I Should be as simple as:

Calling Haskell from Perl

I C, the lingua francaI Haskell FFI to call Haskell from CI Perl Inline::C to call C from PerlI Serialisation

Code Generation

I The call to “use Call::Haskell” triggers extensive code generation:I Haskell FFI and serialisation wrappersI Corresponding C wrappers and create a C libraryI Inline::C wrapper to call the functions in the C libraryI Perl serialisation wrapper

I Compile only when Haskell source code has changed, otherwisecache

Serialisation

I JSON? YAML? MessagePack? No: Read and ShowI Because serialisation needs to be type-aware

I Serialisation of Perl values is done via a custom showH functionI uses Haskell type information via Data.Typeable, serialised using

show, converted to a Perl datastructure in Haskell and deserialisedusing eval.

I De-serialisation of Perl serialised values in Haskell is done viaread

I Serialisation of Haskell values is done via showI string generated by show is converted into Perl code in Haskell

I De-serialisation of Haskell result is done via a custom readHfunction

I uses eval and Perl’s autload feature

Linking

I FFI uses ghc as linkerI Perl requires gcc linkerI ghc does not compile dynamic libraries by defaultI Lots of fun

The Final Picture

1. Perl script calls a2. Perl serialisation wrapper which calls a3. Perl Inline::C wrapper which calls a4. C FFI wrapper which calls a5. Haskell FFI wrapper which calls a6. Haskell serialisation wrapper which calls7. the original Haskell function

I All wrappers are generated and built automatically on loadingCall::Haskell

What About the Types?

I The outlined approach relies on Data.Typeable, Read and Show.I Data.Typeable does not provide information on type constructors

for algebraic types.I So this approach only works for “primitive” types and containers

holding primitive types.I This is already quite a lot, but it’s not good enough.I We could add other types (e.g. Data.Map and Maybe) ad hocI However ...

The Need for Proper Typing

I There is a fundamental issue with variant types.I Given following Haskell type:

data Qual a = Groot a |Klein a

I It is impossible to generate the correctly typed value for Haskellunless the value in Perl is also typed with the same information!

I So we need a type system for Perl...

A Modest Proposal

I I propose to create a type system to type datastructures andfunction signatures in a dynamically typed language (Perl),

I without changing anything to the language itselfI but nevertheless with an acceptable and usable syntax.

I The types must be compatible with Haskell’s types so that it ispossible to call Haskell functions with well-typed arguments andreturn values.

I show and read the typed values in Haskell fashion.

Basic Functionality

I Declare the type of a typed variable:

tv :: T

I Construct a typed value from other typed and/or untyped values:

tv ′ = TC v

I Bind a typed value to a typed variable:

tv = tv ′

I Convert a typed value into an untyped value:

v u= tv

A Simple Type SystemThe type system provides:

I The primitive scalar types Int, Float, String, Bool.

type TI = Inttype TF = Float

I The container types Tuple, Array and Map:

type TT = (T1,T2, ...,TN)

type TA = [T1]

type TM = {Tk ,Tv}

I A mechanism to declare and construct sum and product types:

data TS = T1 |T2 ...TN

data TP = TCP T1 ...TN

Implementation

I To implement this type system in Perl, we useI functions for the type constructors,I a small API of type naming, binding and construction functions.I prototypes to construct the actual types. Prototypes are functions

that return a type descriptor.I lightweight objects to represent the typed values and the type

descriptors returned by the prototypes.

I The implementation relies on two language features:I a function can query the name of its callers (caller in Perl)I data structures can be transformed into lightweight objects (bless

in Perl)

newtype and typename

I To create type constructors:

TC = newtype T (Prototype T1...TN)

I If the type T is polymorphic, the type name can take parameters:

TC = newtype (T a b) (Prototype a b)

I Also used for container types

TC = newtype T (Prototype T1...TN)

I And to create an alias for an existing primitive type

T = newtype Tprim

I To declare the type name T

T = typename

Prototypes

I Algebraic datatypes:I Record: For product types, with optional support for named fieldsI Variant: For sum types

I Container types:I Tuple: For tuplesI Array: For listsI Map: For dictionaries

I Functions: FunctionI To construct primitive types: Scalar

type and bind

I To declare a typed variable

type tv T

If the type is a polymorphic type, we can provide the typeargument:

type tv (T T1)

I To bind a typed variable to a value, we use bind, whichcorresponds to “=” in Haskell.

bind tv tv ′

If the type of a typed variable is primitive, bind can take anuntyped value as the argument:

bind tv v

untype, show and read

I To return an untyped value from a typed value

v = untype tv

I Finally, read and show work as in Haskell

Internals: Type Names andPrototypes

I Type NamesThe typename function returns a tuple of the name of the typeand 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]).I Prototypes

The prototypes take type name functions as arguments, andreturn a type descriptor, implemented as a lightweight object.Prototypes can only be called as argument to newtype, which inits turn can only be called in the body of a type constructorfunction. For example:

IntVar = typenameMkIntVar = newtype IntVar (Record String Int)

The call to Record will return and object of type Record with asattributes the type constructor name and a list of arguments tothe constructor, i.e. typenames

Internals: Typed Value Objects andType Construction

I Typed Value ObjectAn object with attributes Type and Value. The Type attributecontains the information returned by the typename or prototypecalls, the Value attribute is populated by a call to newtype or bind.

I Type ConstructionThe call to newtype typechecks the arguments against the typeinformation returned by the prototype, and returns a typed valueobject, with the Type field provided by the prototype.

Internals: Typing and Binding

I The call to type returns a typed value object with empty Valueattribute, to be filled by a call to bind.

I The call to bind takes a value, typechecks it and stores it in theValue attribute of the typed value.

I If the type is a primitive type or a container type holding primitivetypes, then the value can be untyped and is stored as-is in theValue attribute. In that case, the only checking that is performed ison the type of the container.

I Otherwise, the value must be typed and will be typechecked.

Internals: Typing and Binding

I For example:

type iv IntVarbind iv (MkIntVar “55” 7188 )

The call to MkIntVar will return a typed value object with as Typeattribute a Record object with typename IntVar. The typed valueiv will contain as Type attribute a tuple with as first argument thetypename IntVar. Type checking is simply a comparison of thetypenames.

I In a simpler example:

type iv’ Intbind iv’ (Int 42)

the call to (Int 42) would return a typed value object, and the bindcall would typecheck the typename from the Type attribute withthe typename from iv’

Function Types

I We often want to create typed functions from untyped functions.

type f Int -> Int -> Intbind f (\x y ->x+y)

I But an untyped function can still take and return typed values:

type f (Int,Int) -> Maybe Intbind f (\(x,y) -> if (y/=0) then Just x/y else Nothing)

I We allow both bare and typed values for the argument, andreturn typed values from the function.

I The call to bind creates a new function which takes typedarguments, typechecks, untypes them if required, passes themto the original function, and types the return value if required.

Polymorphic Binary Tree Example

I Polymorphic Binary Tree

Tree = typenameBranch = newtype (Tree a) (Variant Tree a Tree)Leaf = newtype (Tree a) Varianttype tree Tree(Int)bind tree (Branch (Branch Leaf 41 Leaf) 42 Leaf)

I Actual Perl code:

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;

Summary

I Want proper typechecking in Perl?I https://github.com/wimvanderbauwhede/Perl-Functional-Types

I Want to call Haskell from Perl?I https://github.com/wimvanderbauwhede/Perl-Call-Haskell

Thank you! Questions?