Foreign Inline Code in Haskell Manuel M T Chakravarty University of - - PowerPoint PPT Presentation

foreign inline code in haskell
SMART_READER_LITE
LIVE PREVIEW

Foreign Inline Code in Haskell Manuel M T Chakravarty University of - - PowerPoint PPT Presentation

Foreign Inline Code in Haskell Manuel M T Chakravarty University of New South Wales mchakravarty TacticalGrace TacticalGrace justtesting.org 1 30 minute time slot: 25min talking + 5min [15min The Problem; 5min TH+Quasiquoting; 5min


slide-1
SLIDE 1

Manuel M T Chakravarty University of New South Wales

Foreign Inline Code in Haskell

mchakravarty

α

TacticalGrace TacticalGrace justtesting.org

1

30 minute time slot: 25min talking + 5min [15min The Problem; 5min TH+Quasiquoting; 5min Inline Objective-C]

slide-2
SLIDE 2

λ

Shiny new functional language

2

» Imagine, you have got a shiny, new functional language...

slide-3
SLIDE 3

3

» ...and you want to use it to write a great new app...

slide-4
SLIDE 4

λ

4

» ...then you will need to use many existing frameworks and libraries. » Luckily, any serious language will have a foreign function interface! » Haskell standard includes a simple, but versatile FFI

slide-5
SLIDE 5

“Problem solved?”

5

» Does that solve the problem of language interoperability? » Let me explain that at an example...

slide-6
SLIDE 6

“Problem solved?”

N

  • !

5

» Does that solve the problem of language interoperability? » Let me explain that at an example...

slide-7
SLIDE 7

dumpURL :: String -> IO () dumpURL urlString = do urlData <- stringWithContentsOfUrl urlString putStr urlData

What we want to write

6

slide-8
SLIDE 8

dumpURL :: String -> IO () dumpURL urlString = do urlData <- stringWithContentsOfUrl urlString putStr urlData

What we want to write

char *stringWithContentsOfUrlCstub(char *urlString) { NSURL *url = [NSURL URLWithString:urlString]; [NSString stringWithContentsOfURL:url

encoding:NSUTF8StringEncoding error:NULL];

}

What we want to call (& need to put into an extra file)

6

slide-9
SLIDE 9

What we have to write as well…

foreign import stringWithContentsOfURLCstub :: CString -> IO CString stringWithContentsOfURL :: String -> IO String stringWithContentsOfURL url = withCString url $ \urlC -> do resultC <- stringWithContentsOfURLCstub urlC result <- peek resultC free resultC return result

7

» FFI code: If you find it confusing, that’s fine, as I want to argue that you shouldn’t write it in the first place. * Anything extensively relying on foreign frameworks will be a pain [Hybrid languages (eg, F# & Scala) have a difgerent set of trade ofgs, but don’t solve it either.]

slide-10
SLIDE 10

Bridging Libraries

1-to-1 transliteration of types & functions

8

* Bridging or binding libraries transliterate types & functions * Need to be maintained and documented; track multiple versions of base library * Lack of type safety * Tools and dynamic transliteration (by reflection) help

slide-11
SLIDE 11

C➙Haskell

c2hs

A case study

GTK+

9

* c2hs: automation for bridging C libraries to Haskell <https://hackage.haskell.org/package/ c2hs> * Implements large parts of a C compiler front-end * Bridge for the cross-platform Gnome GUI library GTK+ <http://projects.haskell.org/ gtk2hs/>

slide-12
SLIDE 12

10

* AFAIK, currently the only fully featured and properly maintained Haskell GUI library * Haskell GTK+ library is used for realistic applications

slide-13
SLIDE 13

“Does this approach scale?”

11

» Read question * It requires significant resources & constant efgort to track the original library * Application frameworks: enormous and growing footprint

slide-14
SLIDE 14

February 2011 Introduction of GTK+ 3

12

* Base GTK+ development isn’t even particularly fast * Bridging libraries lag behind their base libraries * Another example: Haskell OpenGL library

slide-15
SLIDE 15

February 2011 Introduction of GTK+ 3 December 2013 GTK+ 3 in Haskell Bridge

12

* Base GTK+ development isn’t even particularly fast * Bridging libraries lag behind their base libraries * Another example: Haskell OpenGL library

slide-16
SLIDE 16

13

* Compared to modern application frameworks, GTK+ is small!

slide-17
SLIDE 17

Bridging libraries don’t scale

Too much weight!

14

slide-18
SLIDE 18

“Interoperability is an old problem — maybe an old solution can help?”

15

slide-19
SLIDE 19

16

* Modula-2 with inline 68000 assembly * Assembly code can symbolically refer to Modula-2 entities (e.g., variables)

slide-20
SLIDE 20

16

* Modula-2 with inline 68000 assembly * Assembly code can symbolically refer to Modula-2 entities (e.g., variables)

slide-21
SLIDE 21

16

* Modula-2 with inline 68000 assembly * Assembly code can symbolically refer to Modula-2 entities (e.g., variables)

slide-22
SLIDE 22

“Inline C, C++, Objective-C, … in Haskell?”

17

* We don’t want to build front ends for a few more languages into GHC * How do we share entities between the languages?

slide-23
SLIDE 23

Meta-programming Template Haskell

18

» Generic infrastructure for program manipulation NOTE: I'll run through this quickly; I'll explain the details at the workshop.

slide-24
SLIDE 24

HASKELL WORKSHOP 2002

19

* Template Haskell: Haskell extension implemented by GHC * Useful for: defining macros, code generators, code transformations… * Other languages have their own variants; eg., MetaOCaml » Let’s look at an example…

slide-25
SLIDE 25

HASKELL WORKSHOP 2002

#define

macros

19

* Template Haskell: Haskell extension implemented by GHC * Useful for: defining macros, code generators, code transformations… * Other languages have their own variants; eg., MetaOCaml » Let’s look at an example…

slide-26
SLIDE 26

HASKELL WORKSHOP 2002

#define

macros

[| … |]

code generators

19

* Template Haskell: Haskell extension implemented by GHC * Useful for: defining macros, code generators, code transformations… * Other languages have their own variants; eg., MetaOCaml » Let’s look at an example…

slide-27
SLIDE 27

HASKELL WORKSHOP 2002

#define

macros

[| … |]

code generators

trafo (ConE name) = …

code transformations

19

* Template Haskell: Haskell extension implemented by GHC * Useful for: defining macros, code generators, code transformations… * Other languages have their own variants; eg., MetaOCaml » Let’s look at an example…

slide-28
SLIDE 28

$(sel 1 3) (a, b, c) = a

20

* Meta function executed at splice point, generating spliced code

slide-29
SLIDE 29

$(sel 1 3) (a, b, c) = a

meta-programming function

20

* Meta function executed at splice point, generating spliced code

slide-30
SLIDE 30

$(sel 1 3) (a, b, c) = a

meta-programming function splice

20

* Meta function executed at splice point, generating spliced code

slide-31
SLIDE 31

$(sel 1 3) (a, b, c) = a

meta-programming function splice

\tup -> case tup of {(x, y, z) -> x} :: (a, b, c) -> a

20

* Meta function executed at splice point, generating spliced code

slide-32
SLIDE 32

$(sel 1 3) (a, b, c) = a

meta-programming function splice

\tup -> case tup of {(x, y, z) -> x} :: (a, b, c) -> a $(sel 5 5) (a, b, c, d, e) = e

20

* Meta function executed at splice point, generating spliced code

slide-33
SLIDE 33

$(sel 1 3) (a, b, c) = a

meta-programming function splice

\tup -> case tup of {(x, y, z) -> x} :: (a, b, c) -> a $(sel 5 5) (a, b, c, d, e) = e \tup -> case tup of {(x, y, z, v, w) -> w} :: (a, b, c, d, e) -> e

20

* Meta function executed at splice point, generating spliced code

slide-34
SLIDE 34

sel :: Int -> Int -> ExpQ sel i n = [| \tup -> case tup of {$pat -> $res} |] where pat = tupP (map varP names) res = varE (names !! (i - 1)) names = [mkName $ "v" ++ show i | i <- [1..n]]

21

* Quasiquotations in [|..|] brackets * Explain TH in more detail in the workshop

slide-35
SLIDE 35

sel :: Int -> Int -> ExpQ sel i n = [| \tup -> case tup of {$pat -> $res} |] where pat = tupP (map varP names) res = varE (names !! (i - 1)) names = [mkName $ "v" ++ show i | i <- [1..n]]

type of Haskell expressions

21

* Quasiquotations in [|..|] brackets * Explain TH in more detail in the workshop

slide-36
SLIDE 36

sel :: Int -> Int -> ExpQ sel i n = [| \tup -> case tup of {$pat -> $res} |] where pat = tupP (map varP names) res = varE (names !! (i - 1)) names = [mkName $ "v" ++ show i | i <- [1..n]]

type of Haskell expressions quasi-quotation

21

* Quasiquotations in [|..|] brackets * Explain TH in more detail in the workshop

slide-37
SLIDE 37

sel :: Int -> Int -> ExpQ sel i n = [| \tup -> case tup of {$pat -> $res} |] where pat = tupP (map varP names) res = varE (names !! (i - 1)) names = [mkName $ "v" ++ show i | i <- [1..n]]

type of Haskell expressions quasi-quotation spliced expression (anti quote)

21

* Quasiquotations in [|..|] brackets * Explain TH in more detail in the workshop

slide-38
SLIDE 38

Processing other languages Generic quasi-quotation

22

* Quoting arbitrary languages

slide-39
SLIDE 39

HASKELL WORKSHOP 2007

Quasiquoting for any language you can provide a parser for

23

slide-40
SLIDE 40

Language.C.Quote

add n = [cfun| int addConstant(int x) { return x + $int:n; } |]

24

* QQ for C including some GNU extensions, parts of CUDA & OpenCL, and all of Objective-C

slide-41
SLIDE 41

Language.C.Quote

add n = [cfun| int addConstant(int x) { return x + $int:n; } |]

quasi-quotation identifier

24

* QQ for C including some GNU extensions, parts of CUDA & OpenCL, and all of Objective-C

slide-42
SLIDE 42

Language.C.Quote

add n = [cfun| int addConstant(int x) { return x + $int:n; } |]

quasi-quotation identifier splice identifier

24

* QQ for C including some GNU extensions, parts of CUDA & OpenCL, and all of Objective-C

slide-43
SLIDE 43

mkMap dev aenv fun arr = return $ CUTranslSkel "map" [cunit| $esc:("#include <accelerate_cuda.h>") extern "C" __global__ void map ($params:argIn, $params:argOut) { const int shapeSize = size(shOut); const int gridSize = $exp:(gridSize dev); int ix; for ( ix = $exp:(threadIdx dev) ; ix < shapeSize ; ix += gridSize ) { $items:(dce x .=. get ix) $items:(setOut "ix" .=. f x) } } |] where ...

25

* Accelerate: embedded high-performance array language for GPUs * Combinators as code skeletons (code templates with holes) * Yellow splices/anti-quotes are the holes (parameters) of the template * Doing this with strings or explicit AST construction would be much worse

slide-44
SLIDE 44

mkMap dev aenv fun arr = return $ CUTranslSkel "map" [cunit| $esc:("#include <accelerate_cuda.h>") extern "C" __global__ void map ($params:argIn, $params:argOut) { const int shapeSize = size(shOut); const int gridSize = $exp:(gridSize dev); int ix; for ( ix = $exp:(threadIdx dev) ; ix < shapeSize ; ix += gridSize ) { $items:(dce x .=. get ix) $items:(setOut "ix" .=. f x) } } |] where ...

25

* Accelerate: embedded high-performance array language for GPUs * Combinators as code skeletons (code templates with holes) * Yellow splices/anti-quotes are the holes (parameters) of the template * Doing this with strings or explicit AST construction would be much worse

slide-45
SLIDE 45

mkMap dev aenv fun arr = return $ CUTranslSkel "map" [cunit| $esc:("#include <accelerate_cuda.h>") extern "C" __global__ void map ($params:argIn, $params:argOut) { const int shapeSize = size(shOut); const int gridSize = $exp:(gridSize dev); int ix; for ( ix = $exp:(threadIdx dev) ; ix < shapeSize ; ix += gridSize ) { $items:(dce x .=. get ix) $items:(setOut "ix" .=. f x) } } |] where ...

25

* Accelerate: embedded high-performance array language for GPUs * Combinators as code skeletons (code templates with holes) * Yellow splices/anti-quotes are the holes (parameters) of the template * Doing this with strings or explicit AST construction would be much worse

slide-46
SLIDE 46

Inlining foreign languages Inline C Code

26

slide-47
SLIDE 47

Language.C.Inline

dumpURL :: String -> IO () dumpURL urlString = do urlData <- putStr urlData stringWithContentsOfUrl urlString

27

* Inline Objective-C * All the FFI code is generated automatically * Again, details in the workshop

slide-48
SLIDE 48

Language.C.Inline

dumpURL :: String -> IO () dumpURL urlString = do urlData <- putStr urlData

$(objc ['urlString] ''String [cexp| [NSString stringWithContentsOfURL: [NSURL URLWithString:urlString] encoding:NSUTF8StringEncoding error:NULL] |])

inline Objective-C splice

27

* Inline Objective-C * All the FFI code is generated automatically * Again, details in the workshop

slide-49
SLIDE 49

Inline C

…is simple

28

* Lack of bridge saves a lot of work; instant access to new versions * Bridges require familiarity with native libraries already; inline code leverages that directly * Haskell types are automatically mapped to types of the inline language

slide-50
SLIDE 50

Inline C

…is simple

No bridging library needs to be maintained

28

* Lack of bridge saves a lot of work; instant access to new versions * Bridges require familiarity with native libraries already; inline code leverages that directly * Haskell types are automatically mapped to types of the inline language

slide-51
SLIDE 51

Inline C

…is simple

No extra documentation

No bridging library needs to be maintained

28

* Lack of bridge saves a lot of work; instant access to new versions * Bridges require familiarity with native libraries already; inline code leverages that directly * Haskell types are automatically mapped to types of the inline language

slide-52
SLIDE 52

Inline C

…is simple

No extra documentation Some type safety

No bridging library needs to be maintained

28

* Lack of bridge saves a lot of work; instant access to new versions * Bridges require familiarity with native libraries already; inline code leverages that directly * Haskell types are automatically mapped to types of the inline language

slide-53
SLIDE 53

Language integration by language inlining

types >< state languages

29

Outline workshop * Get familiar with TH and Language.C.Quote * Run through three exercises using inline Objective-C including a graphical REPL

slide-54
SLIDE 54

Workshop Part 1 Template Haskell

30

* http://www.haskell.org/haskellwiki/Template_Haskell * http://hackage.haskell.org/package/template-haskell

slide-55
SLIDE 55

Clone https://github.com/mchakravarty/ylj14-workshop

31

slide-56
SLIDE 56

32

slide-57
SLIDE 57

32

slide-58
SLIDE 58

32

slide-59
SLIDE 59

33

slide-60
SLIDE 60

printf :: String -> ExpQ printf fmt = gen (parse fmt)

33

slide-61
SLIDE 61

printf :: String -> ExpQ printf fmt = gen (parse fmt) data Format = D | S | L String parse :: String -> [Format]

33

slide-62
SLIDE 62

printf :: String -> ExpQ printf fmt = gen (parse fmt) data Format = D | S | L String parse :: String -> [Format] gen :: [Format] -> ExpQ gen [D] = [| \n -> show n |] gen [S] = [| \s -> s |] gen [L s] = [| s |]

33

slide-63
SLIDE 63

Task ❶

A recursive generator

34

slide-64
SLIDE 64

printf/

35

slide-65
SLIDE 65

printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |]

printf/

35

slide-66
SLIDE 66

printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |] gen :: [Format] -> ExpQ -> ExpQ

printf/

35

slide-67
SLIDE 67

printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |] gen :: [Format] -> ExpQ -> ExpQ gen [] prefix = prefix

printf/

35

slide-68
SLIDE 68

printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |] gen :: [Format] -> ExpQ -> ExpQ gen [] prefix = prefix gen (D : fmt) prefix

printf/

35

slide-69
SLIDE 69

printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |] gen :: [Format] -> ExpQ -> ExpQ gen [] prefix = prefix gen (D : fmt) prefix = [| \n -> $(gen fmt [| $prefix ++ show n |]) |]

printf/

35

slide-70
SLIDE 70

printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |] gen :: [Format] -> ExpQ -> ExpQ gen [] prefix = prefix gen (D : fmt) prefix = [| \n -> $(gen fmt [| $prefix ++ show n |]) |] gen (S : fmt) prefix

printf/

35

slide-71
SLIDE 71

printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |] gen :: [Format] -> ExpQ -> ExpQ gen [] prefix = prefix gen (D : fmt) prefix = [| \n -> $(gen fmt [| $prefix ++ show n |]) |] gen (S : fmt) prefix = [| \s -> $(gen fmt [| $prefix ++ s |]) |]

printf/

35

slide-72
SLIDE 72

printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |] gen :: [Format] -> ExpQ -> ExpQ gen [] prefix = prefix gen (D : fmt) prefix = [| \n -> $(gen fmt [| $prefix ++ show n |]) |] gen (S : fmt) prefix = [| \s -> $(gen fmt [| $prefix ++ s |]) |] gen (L s : fmt) prefix

printf/

35

slide-73
SLIDE 73

printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |] gen :: [Format] -> ExpQ -> ExpQ gen [] prefix = prefix gen (D : fmt) prefix = [| \n -> $(gen fmt [| $prefix ++ show n |]) |] gen (S : fmt) prefix = [| \s -> $(gen fmt [| $prefix ++ s |]) |] gen (L s : fmt) prefix = gen fmt [| $prefix ++ s |]

printf/

35

slide-74
SLIDE 74

Workshop Part 2 Quasi-quotation of C variants

36

* http://hackage.haskell.org/package/language-c-quote

slide-75
SLIDE 75

gensum/

37

slide-76
SLIDE 76

genSum :: Name -> Int -> BlockItem genSum arr n = [citem| { int sum = 0; for (int i = 0; i++; i < $n) sum += $id:(show arr)[i]; } |]

gensum/

37

slide-77
SLIDE 77

genSum :: Name -> Int -> BlockItem genSum arr n = [citem| { int sum = 0; for (int i = 0; i++; i < $n) sum += $id:(show arr)[i]; } |]

custom parser for C block items gensum/

37

slide-78
SLIDE 78

genSum :: Name -> Int -> BlockItem genSum arr n = [citem| { int sum = 0; for (int i = 0; i++; i < $n) sum += $id:(show arr)[i]; } |]

custom parser for C block items splicing of a name as an identifier gensum/

37

slide-79
SLIDE 79

genSum :: Name -> Int -> BlockItem genSum arr n = [citem| { int sum = 0; for (int i = 0; i++; i < $n) sum += $id:(show arr)[i]; } |]

custom parser for C block items splicing of a name as an identifier default is to splice as an expression gensum/

37

slide-80
SLIDE 80

Task ❷

Loop unrolling

38

slide-81
SLIDE 81

Replace the for loop by a statement sequence

[cstms| … |]

quote a statement sequence

$stms:…

splice a statement sequence

39

slide-82
SLIDE 82

genSum arr n = [citem| { int sum = 0; $stms:(additions 0) } |] where additions i | i == n = [] | otherwise = [cstms| sum += $id:(show arr)[ $int:i ]; $stms:(additions (i + 1)) |]

Replace the for loop by a statement sequence

[cstms| … |]

quote a statement sequence

$stms:…

splice a statement sequence

39

slide-83
SLIDE 83

Workshop Part 3 Inline Objective-C Code

40

* http://hackage.haskell.org/package/language-c-inline

slide-84
SLIDE 84

{-# LANGUAGE TemplateHaskell, QuasiQuotes #-} import Language.C.Quote.ObjC import Language.C.Inline.ObjC

  • bjc_import ["<Foundation/Foundation.h>"]

nslog :: String -> IO () nslog msg = $(objc ['msg] ''() [cexp| NSLog(@"Here is a message from Haskell: %@”, ! ! ! ! ! ! msg) |])

  • bjc_emit

minimal/

41

* Complete the skeleton in the exercise pack

slide-85
SLIDE 85

{-# LANGUAGE TemplateHaskell, QuasiQuotes #-} import Language.C.Quote.ObjC import Language.C.Inline.ObjC

  • bjc_import ["<Foundation/Foundation.h>"]

nslog :: String -> IO () nslog msg = $(objc ['msg] ''() [cexp| NSLog(@"Here is a message from Haskell: %@”, ! ! ! ! ! ! msg) |])

  • bjc_emit

imports for the generated Objective-C code minimal/

41

* Complete the skeleton in the exercise pack

slide-86
SLIDE 86

{-# LANGUAGE TemplateHaskell, QuasiQuotes #-} import Language.C.Quote.ObjC import Language.C.Inline.ObjC

  • bjc_import ["<Foundation/Foundation.h>"]

nslog :: String -> IO () nslog msg = $(objc ['msg] ''() [cexp| NSLog(@"Here is a message from Haskell: %@”, ! ! ! ! ! ! msg) |])

  • bjc_emit

free variables imports for the generated Objective-C code minimal/

41

* Complete the skeleton in the exercise pack

slide-87
SLIDE 87

{-# LANGUAGE TemplateHaskell, QuasiQuotes #-} import Language.C.Quote.ObjC import Language.C.Inline.ObjC

  • bjc_import ["<Foundation/Foundation.h>"]

nslog :: String -> IO () nslog msg = $(objc ['msg] ''() [cexp| NSLog(@"Here is a message from Haskell: %@”, ! ! ! ! ! ! msg) |])

  • bjc_emit

free variables imports for the generated Objective-C code finalise inline code minimal/

41

* Complete the skeleton in the exercise pack

slide-88
SLIDE 88

Task ❸

Bridging classes

42

slide-89
SLIDE 89

particle/

Particle.hs

Represent Haskell record as ObjC class

Main.hs

Allocate instance and use it

43

slide-90
SLIDE 90

particle/

Particle.hs

Represent Haskell record as ObjC class

Main.hs

Allocate instance and use it

Complete the ObjC class implementation

43

slide-91
SLIDE 91

Task ❹

A simple application

44

slide-92
SLIDE 92

app/

Interpreter.hs

Haskell interface to package ghc

AppDelegate.hs

Haskell-Objective-C bridge

45

slide-93
SLIDE 93

app/

Interpreter.hs

Haskell interface to package ghc

AppDelegate.hs

Haskell-Objective-C bridge

Complete AppDelegate.hs

Run with open -a HSApp.app

45

slide-94
SLIDE 94

Thank you!

46

slide-95
SLIDE 95

Images from

http://wikimedia.org http://openclipart.org

GitHub repo

https://github.com/mchakravarty/language-c-inline

Megamax Modula-2 Screenshot by Dirk Steins