Foreign Inline Code in Haskell - YOW! Conferences and...
-
Upload
phungkhanh -
Category
Documents
-
view
223 -
download
0
Transcript of Foreign Inline Code in Haskell - YOW! Conferences and...
Manuel M T ChakravartyUniversity of New South Wales
Foreign Inline Code in Haskell
mchakravarty
α TacticalGraceTacticalGracejusttesting.org
1
30 minute time slot: 25min talking + 5min[15min The Problem; 5min TH+Quasiquoting; 5min Inline Objective-C]
λ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
“Problem solved?”
5
» Does that solve the problem of language interoperability?» Let me explain that at an example...
“Problem solved?”No!
5
» Does that solve the problem of language interoperability?» Let me explain that at an example...
dumpURL :: String -> IO () dumpURL urlString = do urlData <- stringWithContentsOfUrl urlString putStr urlData
What we want to write
6
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
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 different set of trade offs, but don’t solve it either.]
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
C➙Haskellc2hs
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/>
10
* AFAIK, currently the only fully featured and properly maintained Haskell GUI library* Haskell GTK+ library is used for realistic applications
“Does this approach scale?”
11
» Read question* It requires significant resources & constant effort to track the original library* Application frameworks: enormous and growing footprint
February 2011Introduction of GTK+ 3
12
* Base GTK+ development isn’t even particularly fast* Bridging libraries lag behind their base libraries* Another example: Haskell OpenGL library
February 2011Introduction of GTK+ 3
December 2013GTK+ 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
16
* Modula-2 with inline 68000 assembly* Assembly code can symbolically refer to Modula-2 entities (e.g., variables)
16
* Modula-2 with inline 68000 assembly* Assembly code can symbolically refer to Modula-2 entities (e.g., variables)
16
* Modula-2 with inline 68000 assembly* Assembly code can symbolically refer to Modula-2 entities (e.g., variables)
“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?
Meta-programmingTemplate Haskell
18
» Generic infrastructure for program manipulationNOTE: I'll run through this quickly; I'll explain the details at the workshop.
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…
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…
HASKELL WORKSHOP 2002
#define
macros
[| … |]code
generators19
* 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…
HASKELL WORKSHOP 2002
#define
macros
[| … |]code
generators
trafo (ConE name) = …code
transformations19
* 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…
$(sel 1 3) (a, b, c) = a
meta-programming function
20
* Meta function executed at splice point, generating spliced code
$(sel 1 3) (a, b, c) = a
meta-programming function
splice
20
* Meta function executed at splice point, generating spliced code
$(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
$(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
$(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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Language integration bylanguage 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
Workshop Part 1Template Haskell
30
* http://www.haskell.org/haskellwiki/Template_Haskell* http://hackage.haskell.org/package/template-haskell
printf :: String -> ExpQ printf fmt = gen (parse fmt)
data Format = D | S | L String parse :: String -> [Format]
33
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
printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |]
gen :: [Format] -> ExpQ -> ExpQ
printf/
35
printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |]
gen :: [Format] -> ExpQ -> ExpQ gen [] prefix = prefix
printf/
35
printf :: String -> ExpQ printf fmt = gen (parse fmt) [| "" |]
gen :: [Format] -> ExpQ -> ExpQ gen [] prefix = prefix gen (D : fmt) prefix
printf/
35
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
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
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
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
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
Workshop Part 2Quasi-quotation of C variants
36
* http://hackage.haskell.org/package/language-c-quote
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
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
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
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 spliceas an expression
gensum/
37
Replace the for loop by a statement sequence
[cstms| … |] quote a statement sequence$stms:… splice a statement sequence
39
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
{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
import Language.C.Quote.ObjC import Language.C.Inline.ObjC
objc_import ["<Foundation/Foundation.h>"]
nslog :: String -> IO () nslog msg = $(objc ['msg] ''() [cexp| NSLog(@"Here is a message from Haskell: %@”,! ! ! ! ! ! msg) |])
objc_emit
minimal/
41
* Complete the skeleton in the exercise pack
{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
import Language.C.Quote.ObjC import Language.C.Inline.ObjC
objc_import ["<Foundation/Foundation.h>"]
nslog :: String -> IO () nslog msg = $(objc ['msg] ''() [cexp| NSLog(@"Here is a message from Haskell: %@”,! ! ! ! ! ! msg) |])
objc_emit
imports for the generatedObjective-C code
minimal/
41
* Complete the skeleton in the exercise pack
{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
import Language.C.Quote.ObjC import Language.C.Inline.ObjC
objc_import ["<Foundation/Foundation.h>"]
nslog :: String -> IO () nslog msg = $(objc ['msg] ''() [cexp| NSLog(@"Here is a message from Haskell: %@”,! ! ! ! ! ! msg) |])
objc_emit
free variables
imports for the generatedObjective-C code
minimal/
41
* Complete the skeleton in the exercise pack
{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
import Language.C.Quote.ObjC import Language.C.Inline.ObjC
objc_import ["<Foundation/Foundation.h>"]
nslog :: String -> IO () nslog msg = $(objc ['msg] ''() [cexp| NSLog(@"Here is a message from Haskell: %@”,! ! ! ! ! ! msg) |])
objc_emit
free variables
imports for the generatedObjective-C code
finalise inline code
minimal/
41
* Complete the skeleton in the exercise pack
particle/
Particle.hs Represent Haskell record as ObjC classMain.hs Allocate instance and use it
Complete the ObjC class implementation
43
app/
Interpreter.hs Haskell interface to package ghcAppDelegate.hs Haskell-Objective-C bridge
Complete AppDelegate.hs
Run with open -a HSApp.app
45