git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Use foreign import, not extern. Add foreign import member syntax
commitd49abd21f602d91c728e6271a4bf1a6ddeef10ff authorPhil Freeman <paf31@cantab.net> dateThu 7 Nov 2013, 08:59 (13 years ago) parent4264648
17 files changed+121−93
README.md+16−8 examples/failing/ReadShow.ps+2−2 examples/passing/Console.ps+4−4 examples/passing/ExternData.ps+6−6 examples/passing/FFI.ps+3−3 examples/passing/Person.ps+1−1 examples/passing/TypeSynonyms.ps+1−1 libraries/jquery/jquery.ps+8−8 libraries/prelude/prelude.js−2 libraries/prelude/prelude.ps+48−48 src/Language/PureScript/CodeGen/Externs.hs+4−4 src/Language/PureScript/CodeGen/JS.hs+2 src/Language/PureScript/Declarations.hs+1 src/Language/PureScript/Parser/Common.hs+3−1 src/Language/PureScript/Parser/Declarations.hs+6−3 src/Language/PureScript/TypeChecker.hs+14−1 src/Language/PureScript/TypeChecker/Monad.hs+2−1
README.md+16−8
@@ -41,8 +41,8 @@ The following options are supported:
4141 -o --output
4242 Write the generated Javascript to the specified file
4343
44− -e --externs
45− Write a list of externs declarations to the specified file in addition to generating Javascript output
44+ -e --foreign imports
45+ Write a list of foreign imports declarations to the specified file in addition to generating Javascript output
4646
4747## Motivation
4848
@@ -67,7 +67,7 @@ The following code defines a `Person` data type and a function to generate a str
6767```haskell
6868data Person = Person { name :: String, age :: Number }
6969
70−extern numberToString :: Number -> String
70+foreign import numberToString :: Number -> String
7171
7272showPerson :: Person -> String
7373showPerson = \p -> case p of
@@ -582,18 +582,26 @@ infixr 7 %%
582582
583583## Foreign Function Interface
584584
585−The `extern` keyword declares a value which is defined in Javascript, and its type:
585+The `foreign import` keyword declares a value which is defined in Javascript, and its type:
586586
587587```haskell
588−extern pow :: (Number, Number) -> Number
588+foreign import pow :: (Number, Number) -> Number
589589```
590590
591−To declare a new type with no constructors, use `extern data` and provide the kind:
591+To declare a new type with no constructors, use `foreign import data` and provide the kind:
592592
593593```haskell
594−extern data IO :: * -> *
594+foreign import data IO :: * -> *
595595
596−extern console :: {
596+foreign import console :: {
597597 log :: String -> IO {}
598598}
599599```
600+
601+To alias a name of a field defined on a Javascript type to a PureScript function, use `foreign import member`. For example, to define a function `length` which accesses the ``length` member of an array:
602+
603+```
604+foreign import member "length" length :: forall a. [a] -> Number
605+```
606+
607+
examples/failing/ReadShow.ps+2−2
@@ -1,5 +1,5 @@
1−extern read :: forall a. String -> a
1+foreign import read :: forall a. String -> a
22
3−extern show :: forall a. a -> String
3+foreign import show :: forall a. a -> String
44
55test = \x -> show (read x)
examples/passing/Console.ps+4−4
@@ -1,12 +1,12 @@
1−extern data IO :: * -> *
1+foreign import data IO :: * -> *
22
3−extern ret :: forall a. a -> IO a
3+foreign import ret :: forall a. a -> IO a
44
5−extern (>>=) :: forall a b. IO a -> (a -> IO b) -> IO b
5+foreign import (>>=) :: forall a b. IO a -> (a -> IO b) -> IO b
66
77(*>) = \a -> \b -> a >>= \x -> b
88
9−extern putStrLn :: String -> IO {}
9+foreign import putStrLn :: String -> IO {}
1010
1111replicateM_ :: forall a. Number -> IO a -> IO {}
1212replicateM_ = \n -> \x -> do
examples/passing/ExternData.ps+6−6
@@ -1,13 +1,13 @@
1−extern data IO :: * -> *
1+foreign import data IO :: * -> *
22
3−extern fmap :: forall a b. IO a -> (a -> b) -> IO b
3+foreign import fmap :: forall a b. IO a -> (a -> b) -> IO b
44
5−extern ret :: forall a. a -> IO a
5+foreign import ret :: forall a. a -> IO a
66
7−extern bind :: forall a b. IO a -> (a -> IO b) -> IO b
7+foreign import bind :: forall a b. IO a -> (a -> IO b) -> IO b
88
9−extern showMessage :: String -> IO { }
9+foreign import showMessage :: String -> IO { }
1010
11−extern prompt :: IO String
11+foreign import prompt :: IO String
1212
1313main = prompt `bind` \s -> showMessage s
examples/passing/FFI.ps+3−3
@@ -1,5 +1,5 @@
1−extern foo :: (String) -> String
1+foreign import foo :: String -> String
22
3−bar :: (String) -> String
3+bar :: String -> String
44
5−bar = \x -> foo(x ++ "bar")
5+bar = \x -> foo (x ++ "bar")
examples/passing/Person.ps+1−1
@@ -1,6 +1,6 @@
11data Person = Person { name :: String, age :: Number }
22
3−extern itoa :: Number -> String
3+foreign import itoa :: Number -> String
44
55showPerson :: Person -> String
66showPerson = \p -> case p of
examples/passing/TypeSynonyms.ps+1−1
@@ -23,7 +23,7 @@ fst = {
2323test1 :: forall a b c. Lens (Pair (Pair a b) c) a
2424test1 = composeLenses fst fst
2525
26−extern head :: forall a. Lens [a] a
26+foreign import head :: forall a. Lens [a] a
2727
2828test2 :: forall a b c. Lens (Pair [Pair a b] c) a
2929test2 = composeLenses fst (composeLenses head fst)
libraries/jquery/jquery.ps+8−8
@@ -1,23 +1,23 @@
11-- The jQuery wrapper type
2−extern data JQuery :: *
2+foreign import data JQuery :: *
33
44-- Wrapper function for jQuery selection $('..')
5−extern select :: String -> JQuery
5+foreign import select :: String -> JQuery
66
77-- Wrapper function for jQuery creation e.g. $('<div>')
8−extern create :: String -> JQuery
8+foreign import create :: String -> JQuery
99
1010-- .attr({ ... })
11−extern attr :: forall attr. JQuery -> { | attr } -> JQuery
11+foreign import attr :: forall attr. JQuery -> { | attr } -> JQuery
1212
1313-- .css({ ... })
14−extern css :: forall css. JQuery -> { | css } -> JQuery
14+foreign import css :: forall css. JQuery -> { | css } -> JQuery
1515
1616-- .append(...)
17−extern append :: JQuery -> JQuery -> JQuery
17+foreign import append :: JQuery -> JQuery -> JQuery
1818
1919-- .appendTo(...)
20−extern appendTo :: JQuery -> JQuery -> JQuery
20+foreign import appendTo :: JQuery -> JQuery -> JQuery
2121
2222-- .append(...)
23−extern appendText :: JQuery -> String -> JQuery
23+foreign import appendText :: JQuery -> String -> JQuery
libraries/prelude/prelude.js−2
@@ -1,6 +1,5 @@
11// Arrays
22
3−function lengthi(l) { return l.length; }
43function indexOf(l) { return function (e) { return l.indexOf(e); }; }
54function lastIndexOf(l) { return function (e) { return l.lastIndexOf(e); }; }
65function concat(l1) { return function (l2) { return l1.concat(l2); }; }
@@ -13,7 +12,6 @@ function slice(s) { return function(e) { return function (l) { return l.slice(s,
1312function sort(l) { var l1 = l.slice(); l.sort(); return l1; }
1413function splice(s) { return function(e) { return function(l1) { return function(l2) { return l2.splice(s, e, l1); }; }; }; }
1514
16−function lengthS(s) { return s.length; }
1715function charAt(i) { return function(s) { return s.charAt(i); }; }
1816function indexOfS(s1) { return function(s2) { return s2.indexOf(s2); }; }
1917function lastIndexOfS(s1) { return function(s2) { return s2.lastIndexOf(s2); }; }
libraries/prelude/prelude.ps+48−48
@@ -60,7 +60,7 @@ tailSafe = \xs -> case xs of
6060 [_:xs] -> Just xs
6161 _ -> Nothing
6262
63−extern map :: forall a b. (a -> b) -> [a] -> [b]
63+foreign import map :: forall a b. (a -> b) -> [a] -> [b]
6464
6565foldr :: forall a b. (a -> b -> a) -> a -> [b] -> a
6666foldr = \f -> \a -> \bs -> case bs of
@@ -74,29 +74,29 @@ foldl = \f -> \b -> \as -> do
7474 result = f a result
7575 return result
7676
77−extern length :: forall a. [a] -> Number
77+foreign import member "length" length :: forall a. [a] -> Number
7878
79−extern indexOf :: forall a. [a] -> a -> Number
79+foreign import indexOf :: forall a. [a] -> a -> Number
8080
81−extern lastIndexOf :: forall a. [a] -> a -> Number
81+foreign import lastIndexOf :: forall a. [a] -> a -> Number
8282
83−extern concat :: forall a. [a] -> [a] -> [a]
83+foreign import concat :: forall a. [a] -> [a] -> [a]
8484
85−extern join :: [String] -> String
85+foreign import join :: [String] -> String
8686
87−extern joinWith :: [String] -> String -> String
87+foreign import joinWith :: [String] -> String -> String
8888
89−extern push :: forall a. [a] -> a -> [a]
89+foreign import push :: forall a. [a] -> a -> [a]
9090
91−extern reverse :: forall a. [a] -> [a]
91+foreign import reverse :: forall a. [a] -> [a]
9292
93−extern shift :: forall a. [a] -> [a]
93+foreign import shift :: forall a. [a] -> [a]
9494
95−extern slice :: forall a. Number -> Number -> [a] -> [a]
95+foreign import slice :: forall a. Number -> Number -> [a] -> [a]
9696
97−extern sort :: forall a. [a] -> [a]
97+foreign import sort :: forall a. [a] -> [a]
9898
99−extern splice :: forall a. Number -> Number -> [a] -> [a] -> [a]
99+foreign import splice :: forall a. Number -> Number -> [a] -> [a] -> [a]
100100
101101cons :: forall a. a -> [a] -> [a]
102102cons = \a -> concat([a])
@@ -108,7 +108,7 @@ concatMap = \as -> \f -> do
108108 result = result `concat` f a
109109 return result
110110
111−extern filter :: forall a. [a] -> (a -> Boolean) -> [a]
111+foreign import filter :: forall a. [a] -> (a -> Boolean) -> [a]
112112
113113empty :: forall a. [a] -> Boolean
114114empty = \as -> case as of
@@ -165,75 +165,75 @@ unzip = \ts -> case ts of
165165
166166-- Strings
167167
168−extern lengthS :: String -> Number
168+foreign import member "length" lengthS :: String -> Number
169169
170−extern charAt :: Number -> String -> String
170+foreign import charAt :: Number -> String -> String
171171
172−extern indexOfS :: String -> String -> Number
172+foreign import indexOfS :: String -> String -> Number
173173
174−extern lastIndexOfS :: String -> String -> Number
174+foreign import lastIndexOfS :: String -> String -> Number
175175
176−extern localeCompare :: String -> String -> Number
176+foreign import localeCompare :: String -> String -> Number
177177
178−extern replace :: String -> String -> String -> String
178+foreign import replace :: String -> String -> String -> String
179179
180−extern sliceS :: Number -> Number -> String -> String
180+foreign import sliceS :: Number -> Number -> String -> String
181181
182−extern split :: String -> String -> [String]
182+foreign import split :: String -> String -> [String]
183183
184−extern substr :: Number -> Number -> String -> String
184+foreign import substr :: Number -> Number -> String -> String
185185
186−extern substring :: Number -> Number -> String -> String
186+foreign import substring :: Number -> Number -> String -> String
187187
188−extern toLower :: String -> String
188+foreign import toLower :: String -> String
189189
190−extern toUpper :: String -> String
190+foreign import toUpper :: String -> String
191191
192−extern trim :: String -> String
192+foreign import trim :: String -> String
193193
194194-- Regex
195195
196−extern data Regex :: *
196+foreign import data Regex :: *
197197
198−extern regex :: String -> String -> Regex
198+foreign import regex :: String -> String -> Regex
199199
200−extern test :: Regex -> String -> Boolean
200+foreign import test :: Regex -> String -> Boolean
201201
202−extern match :: Regex -> String -> [String]
202+foreign import match :: Regex -> String -> [String]
203203
204−extern replaceR :: Regex -> String -> String -> String
204+foreign import replaceR :: Regex -> String -> String -> String
205205
206−extern search :: Regex -> String -> Number
206+foreign import search :: Regex -> String -> Number
207207
208208-- Globals
209209
210−extern nan :: Number
210+foreign import nan :: Number
211211
212−extern infinity :: Number
212+foreign import infinity :: Number
213213
214−extern toExponential :: Number -> String
214+foreign import toExponential :: Number -> String
215215
216−extern toFixed :: Number -> Number -> String
216+foreign import toFixed :: Number -> Number -> String
217217
218−extern toPrecision :: Number -> Number -> String
218+foreign import toPrecision :: Number -> Number -> String
219219
220−extern numberToString :: Number -> String
220+foreign import numberToString :: Number -> String
221221
222−extern isNaN :: Number -> Boolean
222+foreign import isNaN :: Number -> Boolean
223223
224−extern isFinite :: Number -> Boolean
224+foreign import isFinite :: Number -> Boolean
225225
226−extern parseFloat :: String -> Number
226+foreign import parseFloat :: String -> Number
227227
228−extern parseInt :: String -> Number
228+foreign import parseInt :: String -> Number
229229
230−extern encodeURIComponent :: String -> String
230+foreign import encodeURIComponent :: String -> String
231231
232−extern decodeURIComponent :: String -> String
232+foreign import decodeURIComponent :: String -> String
233233
234−extern encodeURI :: String -> String
234+foreign import encodeURI :: String -> String
235235
236−extern decodeURI :: String -> String
236+foreign import decodeURI :: String -> String
237237
238238-- Math
239239
@@ -257,5 +257,5 @@ type Math =
257257 , tan :: Number -> Number
258258 }
259259
260−extern math :: Math
260+foreign import math :: Math
261261
src/Language/PureScript/CodeGen/Externs.hs+4−4
@@ -25,11 +25,11 @@ import Language.PureScript.Pretty
2525externToPs :: Environment -> Declaration -> Maybe String
2626externToPs env (ValueDeclaration name _) = do
2727 (ty, _) <- M.lookup name $ names env
28− return $ "extern " ++ show name ++ " :: " ++ prettyPrintPolyType ty
29−externToPs env (ExternDeclaration name ty) =
30− return $ "extern " ++ show name ++ " :: " ++ prettyPrintPolyType ty
28+ return $ "foreign import " ++ show name ++ " :: " ++ prettyPrintPolyType ty
29+externToPs env (ExternMemberDeclaration member name ty) =
30+ return $ "foreign import member " ++ show member ++ " " ++ show name ++ " :: " ++ prettyPrintPolyType ty
3131externToPs env (ExternDataDeclaration name kind) =
32− return $ "extern data " ++ name ++ " :: " ++ prettyPrintKind kind
32+ return $ "foreign import data " ++ name ++ " :: " ++ prettyPrintKind kind
3333externToPs env (TypeSynonymDeclaration name args ty) =
3434 return $ "type " ++ name ++ " " ++ unwords args ++ " = " ++ prettyPrintType ty
3535externToPs _ _ = Nothing
src/Language/PureScript/CodeGen/JS.hs+2
@@ -36,6 +36,8 @@ import Language.PureScript.CodeGen.JS.AST as AST
3636declToJs :: Declaration -> Maybe [JS]
3737declToJs (ValueDeclaration ident (Abs args ret)) = Just [JSFunction (Just ident) args (JSBlock [JSReturn (valueToJs ret)])]
3838declToJs (ValueDeclaration ident val) = Just [JSVariableIntroduction ident (valueToJs val)]
39+declToJs (ExternMemberDeclaration member ident _) =
40+ Just [JSFunction (Just ident) [Ident "value"] (JSBlock [JSReturn (JSAccessor member (JSVar (Ident "value")))])]
3941declToJs (DataDeclaration _ _ ctors) =
4042 Just $ flip map ctors $ \(ctor, maybeTy) ->
4143 case maybeTy of
src/Language/PureScript/Declarations.hs+1
@@ -35,5 +35,6 @@ data Declaration
3535 | TypeDeclaration Ident PolyType
3636 | ValueDeclaration Ident Value
3737 | ExternDeclaration Ident PolyType
38+ | ExternMemberDeclaration String Ident PolyType
3839 | ExternDataDeclaration String Kind
3940 | FixityDeclaration Fixity String deriving (Show, D.Data, D.Typeable)
src/Language/PureScript/Parser/Common.hs+3−1
@@ -44,7 +44,9 @@ reservedNames = [ "case"
4444 , "return"
4545 , "true"
4646 , "false"
47− , "extern"
47+ , "foreign"
48+ , "import"
49+ , "member"
4850 , "forall"
4951 , "do"
5052 , "until"
src/Language/PureScript/Parser/Declarations.hs+6−3
@@ -63,10 +63,13 @@ parseValueDeclaration =
6363 <*> parseValue
6464
6565parseExternDeclaration :: P.Parsec String ParseState Declaration
66−parseExternDeclaration = P.try (reserved "extern") *> indented *>
67− (ExternDataDeclaration <$> (P.try (reserved "data") *> indented *> properName)
68− <*> (lexeme (indented *> P.string "::") *> parseKind)
66+parseExternDeclaration = P.try (reserved "foreign") *> indented *> (reserved "import") *> indented *>
67+ (ExternDataDeclaration <$> (P.try (reserved "data") *> indented *> properName)
68+ <*> (lexeme (indented *> P.string "::") *> parseKind)
6969 <|> ExternDeclaration <$> parseIdent
70+ <*> (lexeme (indented *> P.string "::") *> parsePolyType)
71+ <|> ExternMemberDeclaration <$> (P.try (reserved "member") *> indented *> stringLiteral)
72+ <*> (indented *> parseIdent)
7073 <*> (lexeme (indented *> P.string "::") *> parsePolyType))
7174
7275parseAssociativity :: P.Parsec String ParseState Associativity
src/Language/PureScript/TypeChecker.hs+14−1
@@ -81,8 +81,21 @@ typeCheckAll (ExternDataDeclaration name kind : rest) = do
8181 guardWith (name ++ " is already defined") $ not $ M.member name (types env)
8282 putEnv $ env { types = M.insert name (kind, TypeSynonym) (types env) }
8383 typeCheckAll rest
84+typeCheckAll (ExternMemberDeclaration member name ty : rest) = do
85+ rethrow (("Error in foreign import member declaration " ++ show name ++ ": ") ++) $ do
86+ env <- getEnv
87+ kind <- kindOf ty
88+ guardWith "Expected kind *" $ kind == Star
89+ case M.lookup name (names env) of
90+ Just _ -> throwError $ show name ++ " is already defined"
91+ Nothing -> case ty of
92+ (PolyType _ (Function [_] _)) -> do
93+ putEnv (env { names = M.insert name (ty, Extern) (names env)
94+ , members = M.insert name member (members env) })
95+ _ -> throwError "Foreign member declarations must have function types, with an single argument."
96+ typeCheckAll rest
8497typeCheckAll (ExternDeclaration name ty : rest) = do
85− rethrow (("Error in extern declaration " ++ show name ++ ": ") ++) $ do
98+ rethrow (("Error in foreign import declaration " ++ show name ++ ": ") ++) $ do
8699 env <- getEnv
87100 kind <- kindOf ty
88101 guardWith "Expected kind *" $ kind == Star
src/Language/PureScript/TypeChecker/Monad.hs+2−1
@@ -37,10 +37,11 @@ data Environment = Environment
3737 , types :: M.Map String (Kind, TypeDeclarationKind)
3838 , dataConstructors :: M.Map String PolyType
3939 , typeSynonyms :: M.Map String ([String], Type)
40+ , members :: M.Map Ident String
4041 }
4142
4243emptyEnvironment :: Environment
43−emptyEnvironment = Environment M.empty M.empty M.empty M.empty
44+emptyEnvironment = Environment M.empty M.empty M.empty M.empty M.empty
4445
4546newtype Check a = Check { unCheck :: StateT (Environment, Int) (Either String) a } deriving (Functor, Monad, Applicative, MonadPlus, MonadState (Environment, Int), MonadError String)
4647