git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Use foreign import, not extern. Add foreign import member syntax
d49abd2Phil Freeman
README.md+16−8
@@ -41,8 +41,8 @@ The following options are supported:
41 -o --output
41 -o --output
42 Write the generated Javascript to the specified file
42 Write the generated Javascript to the specified file
43
43
44− -e --externs
44+ -e --foreign imports
45− Write a list of externs declarations to the specified file in addition to generating Javascript output
45+ Write a list of foreign imports declarations to the specified file in addition to generating Javascript output
46
46
47## Motivation
47## Motivation
48
48
@@ -67,7 +67,7 @@ The following code defines a `Person` data type and a function to generate a str
67```haskell
67```haskell
68data Person = Person { name :: String, age :: Number }
68data Person = Person { name :: String, age :: Number }
69
69
70−extern numberToString :: Number -> String
70+foreign import numberToString :: Number -> String
71
71
72showPerson :: Person -> String
72showPerson :: Person -> String
73showPerson = \p -> case p of
73showPerson = \p -> case p of
@@ -582,18 +582,26 @@ infixr 7 %%
582
582
583## Foreign Function Interface
583## Foreign Function Interface
584
584
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:
586
586
587```haskell
587```haskell
588−extern pow :: (Number, Number) -> Number
588+foreign import pow :: (Number, Number) -> Number
589```
589```
590
590
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:
592
592
593```haskell
593```haskell
594−extern data IO :: * -> *
594+foreign import data IO :: * -> *
595
595
596−extern console :: {
596+foreign import console :: {
597 log :: String -> IO {}
597 log :: String -> IO {}
598}
598}
599```
599```
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
2
2
3−extern show :: forall a. a -> String
3+foreign import show :: forall a. a -> String
4
4
5test = \x -> show (read x)
5test = \x -> show (read x)
examples/passing/Console.ps+4−4
@@ -1,12 +1,12 @@
1−extern data IO :: * -> *
1+foreign import data IO :: * -> *
2
2
3−extern ret :: forall a. a -> IO a
3+foreign import ret :: forall a. a -> IO a
4
4
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
6
6
7(*>) = \a -> \b -> a >>= \x -> b
7(*>) = \a -> \b -> a >>= \x -> b
8
8
9−extern putStrLn :: String -> IO {}
9+foreign import putStrLn :: String -> IO {}
10
10
11replicateM_ :: forall a. Number -> IO a -> IO {}
11replicateM_ :: forall a. Number -> IO a -> IO {}
12replicateM_ = \n -> \x -> do
12replicateM_ = \n -> \x -> do
examples/passing/ExternData.ps+6−6
@@ -1,13 +1,13 @@
1−extern data IO :: * -> *
1+foreign import data IO :: * -> *
2
2
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
4
4
5−extern ret :: forall a. a -> IO a
5+foreign import ret :: forall a. a -> IO a
6
6
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
8
8
9−extern showMessage :: String -> IO { }
9+foreign import showMessage :: String -> IO { }
10
10
11−extern prompt :: IO String
11+foreign import prompt :: IO String
12
12
13main = prompt `bind` \s -> showMessage s
13main = 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
2
2
3−bar :: (String) -> String
3+bar :: String -> String
4
4
5−bar = \x -> foo(x ++ "bar")
5+bar = \x -> foo (x ++ "bar")
examples/passing/Person.ps+1−1
@@ -1,6 +1,6 @@
1data Person = Person { name :: String, age :: Number }
1data Person = Person { name :: String, age :: Number }
2
2
3−extern itoa :: Number -> String
3+foreign import itoa :: Number -> String
4
4
5showPerson :: Person -> String
5showPerson :: Person -> String
6showPerson = \p -> case p of
6showPerson = \p -> case p of
examples/passing/TypeSynonyms.ps+1−1
@@ -23,7 +23,7 @@ fst = {
23test1 :: forall a b c. Lens (Pair (Pair a b) c) a
23test1 :: forall a b c. Lens (Pair (Pair a b) c) a
24test1 = composeLenses fst fst
24test1 = composeLenses fst fst
25
25
26−extern head :: forall a. Lens [a] a
26+foreign import head :: forall a. Lens [a] a
27
27
28test2 :: forall a b c. Lens (Pair [Pair a b] c) a
28test2 :: forall a b c. Lens (Pair [Pair a b] c) a
29test2 = composeLenses fst (composeLenses head fst)
29test2 = composeLenses fst (composeLenses head fst)
libraries/jquery/jquery.ps+8−8
@@ -1,23 +1,23 @@
1-- The jQuery wrapper type
1-- The jQuery wrapper type
2−extern data JQuery :: *
2+foreign import data JQuery :: *
3
3
4-- Wrapper function for jQuery selection $('..')
4-- Wrapper function for jQuery selection $('..')
5−extern select :: String -> JQuery
5+foreign import select :: String -> JQuery
6
6
7-- Wrapper function for jQuery creation e.g. $('<div>')
7-- Wrapper function for jQuery creation e.g. $('<div>')
8−extern create :: String -> JQuery
8+foreign import create :: String -> JQuery
9
9
10-- .attr({ ... })
10-- .attr({ ... })
11−extern attr :: forall attr. JQuery -> { | attr } -> JQuery
11+foreign import attr :: forall attr. JQuery -> { | attr } -> JQuery
12
12
13-- .css({ ... })
13-- .css({ ... })
14−extern css :: forall css. JQuery -> { | css } -> JQuery
14+foreign import css :: forall css. JQuery -> { | css } -> JQuery
15
15
16-- .append(...)
16-- .append(...)
17−extern append :: JQuery -> JQuery -> JQuery
17+foreign import append :: JQuery -> JQuery -> JQuery
18
18
19-- .appendTo(...)
19-- .appendTo(...)
20−extern appendTo :: JQuery -> JQuery -> JQuery
20+foreign import appendTo :: JQuery -> JQuery -> JQuery
21
21
22-- .append(...)
22-- .append(...)
23−extern appendText :: JQuery -> String -> JQuery
23+foreign import appendText :: JQuery -> String -> JQuery
libraries/prelude/prelude.js−2
@@ -1,6 +1,5 @@
1// Arrays
1// Arrays
2
2
3−function lengthi(l) { return l.length; }
4function indexOf(l) { return function (e) { return l.indexOf(e); }; }
3function indexOf(l) { return function (e) { return l.indexOf(e); }; }
5function lastIndexOf(l) { return function (e) { return l.lastIndexOf(e); }; }
4function lastIndexOf(l) { return function (e) { return l.lastIndexOf(e); }; }
6function concat(l1) { return function (l2) { return l1.concat(l2); }; }
5function 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,
13function sort(l) { var l1 = l.slice(); l.sort(); return l1; }
12function sort(l) { var l1 = l.slice(); l.sort(); return l1; }
14function splice(s) { return function(e) { return function(l1) { return function(l2) { return l2.splice(s, e, l1); }; }; }; }
13function splice(s) { return function(e) { return function(l1) { return function(l2) { return l2.splice(s, e, l1); }; }; }; }
15
14
16−function lengthS(s) { return s.length; }
17function charAt(i) { return function(s) { return s.charAt(i); }; }
15function charAt(i) { return function(s) { return s.charAt(i); }; }
18function indexOfS(s1) { return function(s2) { return s2.indexOf(s2); }; }
16function indexOfS(s1) { return function(s2) { return s2.indexOf(s2); }; }
19function lastIndexOfS(s1) { return function(s2) { return s2.lastIndexOf(s2); }; }
17function lastIndexOfS(s1) { return function(s2) { return s2.lastIndexOf(s2); }; }
libraries/prelude/prelude.ps+48−48
@@ -60,7 +60,7 @@ tailSafe = \xs -> case xs of
60 [_:xs] -> Just xs
60 [_:xs] -> Just xs
61 _ -> Nothing
61 _ -> Nothing
62
62
63−extern map :: forall a b. (a -> b) -> [a] -> [b]
63+foreign import map :: forall a b. (a -> b) -> [a] -> [b]
64
64
65foldr :: forall a b. (a -> b -> a) -> a -> [b] -> a
65foldr :: forall a b. (a -> b -> a) -> a -> [b] -> a
66foldr = \f -> \a -> \bs -> case bs of
66foldr = \f -> \a -> \bs -> case bs of
@@ -74,29 +74,29 @@ foldl = \f -> \b -> \as -> do
74 result = f a result
74 result = f a result
75 return result
75 return result
76
76
77−extern length :: forall a. [a] -> Number
77+foreign import member "length" length :: forall a. [a] -> Number
78
78
79−extern indexOf :: forall a. [a] -> a -> Number
79+foreign import indexOf :: forall a. [a] -> a -> Number
80
80
81−extern lastIndexOf :: forall a. [a] -> a -> Number
81+foreign import lastIndexOf :: forall a. [a] -> a -> Number
82
82
83−extern concat :: forall a. [a] -> [a] -> [a]
83+foreign import concat :: forall a. [a] -> [a] -> [a]
84
84
85−extern join :: [String] -> String
85+foreign import join :: [String] -> String
86
86
87−extern joinWith :: [String] -> String -> String
87+foreign import joinWith :: [String] -> String -> String
88
88
89−extern push :: forall a. [a] -> a -> [a]
89+foreign import push :: forall a. [a] -> a -> [a]
90
90
91−extern reverse :: forall a. [a] -> [a]
91+foreign import reverse :: forall a. [a] -> [a]
92
92
93−extern shift :: forall a. [a] -> [a]
93+foreign import shift :: forall a. [a] -> [a]
94
94
95−extern slice :: forall a. Number -> Number -> [a] -> [a]
95+foreign import slice :: forall a. Number -> Number -> [a] -> [a]
96
96
97−extern sort :: forall a. [a] -> [a]
97+foreign import sort :: forall a. [a] -> [a]
98
98
99−extern splice :: forall a. Number -> Number -> [a] -> [a] -> [a]
99+foreign import splice :: forall a. Number -> Number -> [a] -> [a] -> [a]
100
100
101cons :: forall a. a -> [a] -> [a]
101cons :: forall a. a -> [a] -> [a]
102cons = \a -> concat([a])
102cons = \a -> concat([a])
@@ -108,7 +108,7 @@ concatMap = \as -> \f -> do
108 result = result `concat` f a
108 result = result `concat` f a
109 return result
109 return result
110
110
111−extern filter :: forall a. [a] -> (a -> Boolean) -> [a]
111+foreign import filter :: forall a. [a] -> (a -> Boolean) -> [a]
112
112
113empty :: forall a. [a] -> Boolean
113empty :: forall a. [a] -> Boolean
114empty = \as -> case as of
114empty = \as -> case as of
@@ -165,75 +165,75 @@ unzip = \ts -> case ts of
165
165
166-- Strings
166-- Strings
167
167
168−extern lengthS :: String -> Number
168+foreign import member "length" lengthS :: String -> Number
169
169
170−extern charAt :: Number -> String -> String
170+foreign import charAt :: Number -> String -> String
171
171
172−extern indexOfS :: String -> String -> Number
172+foreign import indexOfS :: String -> String -> Number
173
173
174−extern lastIndexOfS :: String -> String -> Number
174+foreign import lastIndexOfS :: String -> String -> Number
175
175
176−extern localeCompare :: String -> String -> Number
176+foreign import localeCompare :: String -> String -> Number
177
177
178−extern replace :: String -> String -> String -> String
178+foreign import replace :: String -> String -> String -> String
179
179
180−extern sliceS :: Number -> Number -> String -> String
180+foreign import sliceS :: Number -> Number -> String -> String
181
181
182−extern split :: String -> String -> [String]
182+foreign import split :: String -> String -> [String]
183
183
184−extern substr :: Number -> Number -> String -> String
184+foreign import substr :: Number -> Number -> String -> String
185
185
186−extern substring :: Number -> Number -> String -> String
186+foreign import substring :: Number -> Number -> String -> String
187
187
188−extern toLower :: String -> String
188+foreign import toLower :: String -> String
189
189
190−extern toUpper :: String -> String
190+foreign import toUpper :: String -> String
191
191
192−extern trim :: String -> String
192+foreign import trim :: String -> String
193
193
194-- Regex
194-- Regex
195
195
196−extern data Regex :: *
196+foreign import data Regex :: *
197
197
198−extern regex :: String -> String -> Regex
198+foreign import regex :: String -> String -> Regex
199
199
200−extern test :: Regex -> String -> Boolean
200+foreign import test :: Regex -> String -> Boolean
201
201
202−extern match :: Regex -> String -> [String]
202+foreign import match :: Regex -> String -> [String]
203
203
204−extern replaceR :: Regex -> String -> String -> String
204+foreign import replaceR :: Regex -> String -> String -> String
205
205
206−extern search :: Regex -> String -> Number
206+foreign import search :: Regex -> String -> Number
207
207
208-- Globals
208-- Globals
209
209
210−extern nan :: Number
210+foreign import nan :: Number
211
211
212−extern infinity :: Number
212+foreign import infinity :: Number
213
213
214−extern toExponential :: Number -> String
214+foreign import toExponential :: Number -> String
215
215
216−extern toFixed :: Number -> Number -> String
216+foreign import toFixed :: Number -> Number -> String
217
217
218−extern toPrecision :: Number -> Number -> String
218+foreign import toPrecision :: Number -> Number -> String
219
219
220−extern numberToString :: Number -> String
220+foreign import numberToString :: Number -> String
221
221
222−extern isNaN :: Number -> Boolean
222+foreign import isNaN :: Number -> Boolean
223
223
224−extern isFinite :: Number -> Boolean
224+foreign import isFinite :: Number -> Boolean
225
225
226−extern parseFloat :: String -> Number
226+foreign import parseFloat :: String -> Number
227
227
228−extern parseInt :: String -> Number
228+foreign import parseInt :: String -> Number
229
229
230−extern encodeURIComponent :: String -> String
230+foreign import encodeURIComponent :: String -> String
231
231
232−extern decodeURIComponent :: String -> String
232+foreign import decodeURIComponent :: String -> String
233
233
234−extern encodeURI :: String -> String
234+foreign import encodeURI :: String -> String
235
235
236−extern decodeURI :: String -> String
236+foreign import decodeURI :: String -> String
237
237
238-- Math
238-- Math
239
239
@@ -257,5 +257,5 @@ type Math =
257 , tan :: Number -> Number
257 , tan :: Number -> Number
258 }
258 }
259
259
260−extern math :: Math
260+foreign import math :: Math
261
261
src/Language/PureScript/CodeGen/Externs.hs+4−4
@@ -25,11 +25,11 @@ import Language.PureScript.Pretty
25externToPs :: Environment -> Declaration -> Maybe String
25externToPs :: Environment -> Declaration -> Maybe String
26externToPs env (ValueDeclaration name _) = do
26externToPs env (ValueDeclaration name _) = do
27 (ty, _) <- M.lookup name $ names env
27 (ty, _) <- M.lookup name $ names env
28− return $ "extern " ++ show name ++ " :: " ++ prettyPrintPolyType ty
28+ return $ "foreign import " ++ show name ++ " :: " ++ prettyPrintPolyType ty
29−externToPs env (ExternDeclaration name ty) =
29+externToPs env (ExternMemberDeclaration member name ty) =
30− return $ "extern " ++ show name ++ " :: " ++ prettyPrintPolyType ty
30+ return $ "foreign import member " ++ show member ++ " " ++ show name ++ " :: " ++ prettyPrintPolyType ty
31externToPs env (ExternDataDeclaration name kind) =
31externToPs env (ExternDataDeclaration name kind) =
32− return $ "extern data " ++ name ++ " :: " ++ prettyPrintKind kind
32+ return $ "foreign import data " ++ name ++ " :: " ++ prettyPrintKind kind
33externToPs env (TypeSynonymDeclaration name args ty) =
33externToPs env (TypeSynonymDeclaration name args ty) =
34 return $ "type " ++ name ++ " " ++ unwords args ++ " = " ++ prettyPrintType ty
34 return $ "type " ++ name ++ " " ++ unwords args ++ " = " ++ prettyPrintType ty
35externToPs _ _ = Nothing
35externToPs _ _ = Nothing
src/Language/PureScript/CodeGen/JS.hs+2
@@ -36,6 +36,8 @@ import Language.PureScript.CodeGen.JS.AST as AST
36declToJs :: Declaration -> Maybe [JS]
36declToJs :: Declaration -> Maybe [JS]
37declToJs (ValueDeclaration ident (Abs args ret)) = Just [JSFunction (Just ident) args (JSBlock [JSReturn (valueToJs ret)])]
37declToJs (ValueDeclaration ident (Abs args ret)) = Just [JSFunction (Just ident) args (JSBlock [JSReturn (valueToJs ret)])]
38declToJs (ValueDeclaration ident val) = Just [JSVariableIntroduction ident (valueToJs val)]
38declToJs (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")))])]
39declToJs (DataDeclaration _ _ ctors) =
41declToJs (DataDeclaration _ _ ctors) =
40 Just $ flip map ctors $ \(ctor, maybeTy) ->
42 Just $ flip map ctors $ \(ctor, maybeTy) ->
41 case maybeTy of
43 case maybeTy of
src/Language/PureScript/Declarations.hs+1
@@ -35,5 +35,6 @@ data Declaration
35 | TypeDeclaration Ident PolyType
35 | TypeDeclaration Ident PolyType
36 | ValueDeclaration Ident Value
36 | ValueDeclaration Ident Value
37 | ExternDeclaration Ident PolyType
37 | ExternDeclaration Ident PolyType
38+ | ExternMemberDeclaration String Ident PolyType
38 | ExternDataDeclaration String Kind
39 | ExternDataDeclaration String Kind
39 | FixityDeclaration Fixity String deriving (Show, D.Data, D.Typeable)
40 | FixityDeclaration Fixity String deriving (Show, D.Data, D.Typeable)
src/Language/PureScript/Parser/Common.hs+3−1
@@ -44,7 +44,9 @@ reservedNames = [ "case"
44 , "return"
44 , "return"
45 , "true"
45 , "true"
46 , "false"
46 , "false"
47− , "extern"
47+ , "foreign"
48+ , "import"
49+ , "member"
48 , "forall"
50 , "forall"
49 , "do"
51 , "do"
50 , "until"
52 , "until"
src/Language/PureScript/Parser/Declarations.hs+6−3
@@ -63,10 +63,13 @@ parseValueDeclaration =
63 <*> parseValue
63 <*> parseValue
64
64
65parseExternDeclaration :: P.Parsec String ParseState Declaration
65parseExternDeclaration :: P.Parsec String ParseState Declaration
66−parseExternDeclaration = P.try (reserved "extern") *> indented *>
66+parseExternDeclaration = P.try (reserved "foreign") *> indented *> (reserved "import") *> indented *>
67− (ExternDataDeclaration <$> (P.try (reserved "data") *> indented *> properName)
67+ (ExternDataDeclaration <$> (P.try (reserved "data") *> indented *> properName)
68− <*> (lexeme (indented *> P.string "::") *> parseKind)
68+ <*> (lexeme (indented *> P.string "::") *> parseKind)
69 <|> ExternDeclaration <$> parseIdent
69 <|> ExternDeclaration <$> parseIdent
70+ <*> (lexeme (indented *> P.string "::") *> parsePolyType)
71+ <|> ExternMemberDeclaration <$> (P.try (reserved "member") *> indented *> stringLiteral)
72+ <*> (indented *> parseIdent)
70 <*> (lexeme (indented *> P.string "::") *> parsePolyType))
73 <*> (lexeme (indented *> P.string "::") *> parsePolyType))
71
74
72parseAssociativity :: P.Parsec String ParseState Associativity
75parseAssociativity :: P.Parsec String ParseState Associativity
src/Language/PureScript/TypeChecker.hs+14−1
@@ -81,8 +81,21 @@ typeCheckAll (ExternDataDeclaration name kind : rest) = do
81 guardWith (name ++ " is already defined") $ not $ M.member name (types env)
81 guardWith (name ++ " is already defined") $ not $ M.member name (types env)
82 putEnv $ env { types = M.insert name (kind, TypeSynonym) (types env) }
82 putEnv $ env { types = M.insert name (kind, TypeSynonym) (types env) }
83 typeCheckAll rest
83 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
84typeCheckAll (ExternDeclaration name ty : rest) = do
97typeCheckAll (ExternDeclaration name ty : rest) = do
85− rethrow (("Error in extern declaration " ++ show name ++ ": ") ++) $ do
98+ rethrow (("Error in foreign import declaration " ++ show name ++ ": ") ++) $ do
86 env <- getEnv
99 env <- getEnv
87 kind <- kindOf ty
100 kind <- kindOf ty
88 guardWith "Expected kind *" $ kind == Star
101 guardWith "Expected kind *" $ kind == Star
src/Language/PureScript/TypeChecker/Monad.hs+2−1
@@ -37,10 +37,11 @@ data Environment = Environment
37 , types :: M.Map String (Kind, TypeDeclarationKind)
37 , types :: M.Map String (Kind, TypeDeclarationKind)
38 , dataConstructors :: M.Map String PolyType
38 , dataConstructors :: M.Map String PolyType
39 , typeSynonyms :: M.Map String ([String], Type)
39 , typeSynonyms :: M.Map String ([String], Type)
40+ , members :: M.Map Ident String
40 }
41 }
41
42
42emptyEnvironment :: Environment
43emptyEnvironment :: 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
44
45
45newtype Check a = Check { unCheck :: StateT (Environment, Int) (Either String) a } deriving (Functor, Monad, Applicative, MonadPlus, MonadState (Environment, Int), MonadError String)
46newtype Check a = Check { unCheck :: StateT (Environment, Int) (Either String) a } deriving (Functor, Monad, Applicative, MonadPlus, MonadState (Environment, Int), MonadError String)
46
47