faylang / fay Goto Github PK
View Code? Open in Web Editor NEWA proper subset of Haskell that compiles to JavaScript
Home Page: https://github.com/faylang/fay/wiki
License: BSD 3-Clause "New" or "Revised" License
A proper subset of Haskell that compiles to JavaScript
Home Page: https://github.com/faylang/fay/wiki
License: BSD 3-Clause "New" or "Revised" License
Guard support:
g x | x ==1 = 0
| otherwise = 1
Seems to be missing, fay gives the error:
fay: UnsupportedRhs (GuardedRhss [GuardedRhs (SrcLoc {srcFilename = ".hs", srcLine = 3, srcColumn = 5}) [Qualifier (InfixApp (Var (UnQual (Ident "x"))) (QVarOp (UnQual (Symbol "=="))) (Lit (Int 1)))](Lit %28Int 0%29),GuardedRhs (SrcLoc {srcFilename = ".hs", srcLine = 4, srcColumn = 5}) [Qualifier (Var (UnQual (Ident "otherwise")))](Lit %28Int 1%29)])
This is with GHC 7.4.2 on a x86_64 Linux system.
I see that when the return type is (), its just an empty string. In the dom example, I see that an array of Elements is "array". What if it is a single element?
data R = R Int Int
f = 1 `R` 2
=> UnsupportedOperator (QConOp (UnQual (Ident "R")))
f (1 `R` 2) = 1
=> UnsupportedPattern (PInfixApp (PLit (Int 1)) (UnQual (Ident "R")) (PLit (Int 2)))
a x y = x + y
f = 1 `a` 2
=> UnsupportedOperator (QVarOp (UnQual (Ident "a")))
data X = Int :-> Int
=> UnsupportedDeclaration (DataDecl (SrcLoc {...}) DataType [] (Ident "X") [] [QualConDecl (SrcLoc {...}) [] [] (InfixConDecl (UnBangedTy (TyCon (UnQual (Ident "Int")))) (Symbol ":->") (UnBangedTy (TyCon (UnQual (Ident "Int")))))] [])
f (1 :-> 2) = 1
=> UnsupportedPattern (PInfixApp (PLit (Int 1)) (UnQual (Symbol ":->")) (PLit (Int 2)))
Running 'fay' with no arguments produces no output. That means it's impossible to determine how to use it without visiting the website.
Some documentation should be displayed when 'fay' is run with no files or with a '-help' argument.
I see references to fromInteger in Prelude, which if I understood correctly is for changing types for GHC. However there is no handling for them on the javascript-side. I haven't tested, but I assume fromRational suffers from the same problem.
{-# LANGUAGE NoImplicitPrelude #-}
module Test where
import Language.Fay.Prelude
import Language.Fay.FFI
print :: Foreign a => a -> Fay ()
print = foreignFay "console.log" ""
main :: Fay ()
main = print $ show $ fromInteger 5
ReferenceError: fromInteger is not defined
Hey,
I was trying the alert example the examples subdirectory. I did:
ghc alert.hs
[1 of 1] Compiling Main ( alert.hs, alert.o )
alert.hs:8:9: Not in scope: type constructor or class `IO'
fay alert.hs
Which gives an alert.js. But if I add it to an html page and run it in chromium, I do not get anything (no alert window).
I am on ubuntu 12.04 64bit.
If there a way I can produce code like this:
var k, v;
for (k in obj) {
v = obj[k];
f(v);
}
I'm trying to whip up something that I can use to semi auto generate FFI bindings and I thought it would be fun to dog food.
I modified tests/16.hs to look like this:
data Person = Person String String Int
p' = Person "Chris" "Done" 13
main = print (case p' of
Person "Chris" "Done" 13 -> "Hello!")
print :: String -> Fay ()
print = ffi "console.log(%1)"
The generated 16.js gives the variable definition the correct name:
var p$39$ = new $(function() { ...
But in the pattern match:
...
return _(print)((function($_p'){if ((_($_p'))[0].name === "Person") {
...
There is a parse error.
[jpmayer@jpmayer-ASUS-fedora learn-fay]$ cat console.hs
{-# LANGUAGE NoImplicitPrelude #-}
module Console where
import Language.Fay.FFI
import Language.Fay.Prelude
main = print "Hello, World!"
-- | Print using console.log.
print :: String -> Fay ()
print = ffi "console.log(%1)"
[jpmayer@jpmayer-ASUS-fedora learn-fay]$ fay --autorun console.hs
[jpmayer@jpmayer-ASUS-fedora learn-fay]$ nodejs --version
v0.6.18
[jpmayer@jpmayer-ASUS-fedora learn-fay]$ nodejs console.js
[jpmayer@jpmayer-ASUS-fedora learn-fay]$
Out of the box Haskell Platform (ghc 7.0.4) running on fedora 17.
l = [i | i <- [1,2]]
produces
UnsupportedExpression (ListComp (Var (UnQual (Ident "i"))) [QualStmt (Generator (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 9, srcColumn = 10}) (PVar (Ident "i")) (List [Lit (Int 1),Lit (Int 2)]))])
Fay should print === instead of == for the JsEq node. == can perform type coercion in javascript. === is a strict comparison and performs no type conversions.
I pushed a change to allow record definitions to be imported and the records to be used, but the emitFayToJs and emitJsToFay functions update the state only when a declaration occurs, so these changes are dropped when a module is imported, and records can not be (de)serialized when they don't belong to the main module.
I see two possible solutions:
Creating this ticket to track this, even if I end up adding it myself (still no guarantees!).
Fay
monad an instance of HSX.XMLGenerator.XMLGen
so that GHC can type-check modules by using the trhsx
preprocessor.Benefits:
#37 implemented some of it. But this still isn't supported:
f :: String -> String
f x = friends ++ family
where friends = x
family = " and family"
main = print (f "my friends")
causes
UnsupportedWhereInMatch (Match (SrcLoc {...}) (Ident "f") [PVar (Ident "x")] Nothing (UnGuardedRhs (InfixApp (Var (UnQual (Ident "friends"))) (QVarOp (UnQual (Symbol "++"))) (Var (UnQual (Ident "family"))))) (BDecls [PatBind (SrcLoc {...}) (PVar (Ident "friends")) Nothing (UnGuardedRhs (Var (UnQual (Ident "x")))) (BDecls []),PatBind (SrcLoc {...}) (PVar (Ident "family")) Nothing (UnGuardedRhs (Lit (String " and family"))) (BDecls [])]))
@sixohsix would you be interested in helping out some more? :)
I'll just include the relevant excerpt of our email exchange here:
On an unrelated note, I was trying to write the variant of addClass that takes a callback function but couldn't get it to work. My naive attempt was this:
addClassWith :: (Double -> String -> Fay String) -> JQuery -> Fay JQuery addClassWith = ffi "%2.addClass(_(%1))" FayNone
Ah, good point. It should be merely %2.addClass(%1). That works for me, but the string is not serialized into a Fay string, so I need to add support to unserialize the arguments back to Fay from the callback. For now what you can do is:
addClassWith :: (Double -> String -> Fay String) -> JQuery -> Fay JQuery
addClassWith = ffi "%2.addClass(function(x,i){ return %1(x,Fay$$list(i)); })" FayNone
Until I've added unserializing for callbacks. Can you make a ticket for it on Github?
That does some simple client-server communication.
For big speed win on numbers.
data R = R { x :: Integer, y :: String }
r1 :: R
r1 = R { x = 1, y = "a" }
=> UnsupportedExpression (RecConstr (UnQual (Ident "R")) [FieldUpdate (UnQual (Ident "x")) (Lit (Int 1)),FieldUpdate (UnQual (Ident "y")) (Lit (String "a"))])
r2 :: R
r2 = R 2 "y"
r3 :: R
r3 = r2 { x = 3 }
=> UnsupportedExpression (RecUpdate (Var (UnQual (Ident "r2"))) [FieldUpdate (UnQual (Ident "x")) (Lit (Int 3))])
Where syntax is not supported, but no warning is given until you try to run the script.
{-# LANGUAGE NoImplicitPrelude #-}
module Test where
import Language.Fay.Prelude
import Language.Fay.FFI
fac :: Integer -> Integer
fac n = go 1 n
where go acc 1 = acc
go acc n = go (acc * n) (n - 1)
print :: Foreign a => a -> Fay ()
print = foreignFay "console.log" ""
main :: Fay ()
main = print $ show $ fac 5
ReferenceError: go is not defined
The docs can be treated as a general report for development and newbies.
This compiles with GHC but fay errors out with "Record name was not found in stateRecords, should be impossible" since the record definition is parsed after the function. Also would be nice to get the name of the record in the error message.
{-# LANGUAGE NoImplicitPrelude #-}
module M where
f C = 1
data R = C
Edit: Is it possible to reorder the parse tree so records occur first, or will this just cause another similar issue later? Perhaps we can make an initial pass-through and collect all the type/declaration information we need?
See code and output below - shouldn't the behavior be "Import syntax not supported"? Referring to line 177 in src/Language/Fay.hs
[jpmayer@jpmayer-ASUS-fedora learn-fay]$ cat makefile
all: Alert.js Write.js
Write.js: Write.hs
./fay Write.hs
Alert.js: Alert.hs Write.js
./fay --html-wrapper --autorun Alert.hs
[jpmayer@jpmayer-ASUS-fedora learn-fay]$ cat Write.hs
{-# LANGUAGE NoImplicitPrelude #-}
module Write where
import Language.Fay.FFI
write :: String -> Fay ()
write = ffi "document.write(%1)"
[jpmayer@jpmayer-ASUS-fedora learn-fay]$ cat Alert.hs
{-# LANGUAGE NoImplicitPrelude #-}
module Alert where
import Language.Fay.FFI
import Language.Fay.Prelude
import Write
main = do alert "Hello, World!"
alert "Dwubwubwub"
-- | Alert using console.log.
alert :: String -> Fay ()
alert = ffi "window.alert(%1)"
[jpmayer@jpmayer-ASUS-fedora learn-fay]$ make
./fay --html-wrapper --autorun Alert.hs
fay: Could not find import: Write
make: *** [Alert.js] Error 1
[jpmayer@jpmayer-ASUS-fedora learn-fay]$
It's not obvious from looking at the code whether it's possible to call arbitrary functions or methods in Node.js. An example would really help.
Hey,
Can I somehow make a function call with multiple parameters or even a method call? Like:
context2d.arc(x,y,r,minPhi,maxPhi,ccw);
When trying to convert the snippet:
g [] = 0 g [x] = x g [x,y] = max x y g (x:xs@(y:ys)) = max (x + g ys) (g xs)
I get:
fay: UnsupportedPattern (PAsPat (Ident "xs") (PParen (PInfixApp (PVar (Ident "y")) (Special Cons) (PVar (Ident "ys")))))
Would someone be so kind and explain what I'm doing wrong / what I need to change?
I successfully generated a .js file with fay and I noticed that nub() is part of fay's "standard library". However I tried this in Chrome's console:
main.nub([2,1,1,3]).value().value()
expecting to receive back [2,1,3] which is what Haskell's nub would give.
Instead I'm getting the empty list back, and with a red warning sign to the left of it.
I'm clearly doing something wrong, but I don't know what.
Any help is appreciated.
Consider the following code:
selectObject :: (Foreign a) => a -> Fay JQuery
selectObject = ffi "jQuery(%1)" FayNone
select :: String -> Fay JQuery
select = ffi "jQuery(%1)" FayNone
This performs as expected, allowing arbitrary objects to be made into jQuery objects, and allowing haskell strings to be used as selectors.
One would be tempted to refactor by declaring "select = selectObject". However, doing so prevents select from working.
The generated code for the original case:
var select = function($_a){return new $(function(){return new Fay$$Monad(Fay$$unserialize("",jQuery(Fay$$serialize(StringType,$_a))));});};
And the refactored case:
var select = new $(function(){return selectObject;});
Not hard, just needs adding.
When I try to compile the alert example, I get this:
ghc alert.hs
[1 of 1] Compiling Alert ( alert.hs, alert.o )
fay alert.hs
fay: FfiNeedsTypeSig (PatBind (SrcLoc {srcFilename = "<unknown>.hs", srcLine = 9, srcColumn = 1}) (PVar (Ident "main")) Nothing (UnGuardedRhs (App (Var (UnQual (Ident "alert"))) (Lit (String "Hello, World!")))) (BDecls []))
Operators /, <= and >= are missing from the Language.Fay.Prelude.
<= and >= aren't exported in the js, but / is.
module Test where
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE NoImplicitPrelude #-}
causes
ParseError
(SrcLoc{srcFilename = "<unknown>.hs", srcLine = 4, srcColumn = 1})
"Parse error: {-# LANGUAGE"
is this a problem with src-exts?
E.g. FFI stuff, laziness, etc.
Hey,
I want to share information between different callbacks (keyup, keydown, setInterval). In Javascript I would store this information in global variables.
So I need to create "globalVariables". My idea is to write a javascript function:
var allObjects = {}
function jsSaveGlobalObject(name, obj) {
allObjects[name] = obj;
}
function jsLoadGlobalObject(name) {
return allObjects[name];
}
But the signature would have an open type variable in haskell:
saveGlobalObject :: String -> a -> Fay ()
Or it needs to be of type (JsPtr a). I am guessing that is the right approach. But how do construct and deconstruct a JsPtr?
As far as a I can see the constructor is not accessible ...
Thanks!
Nathan
This is a nifty project! I write JavaScript code professionally and I also really like Haskell. I'm digging this combination of the two.
I think that it would be helpful to map Haskell modules to JavaScript modules. That would improve interoperability with native JavaScript by allowing JavaScript code to reference and call into compiled Haskell code and vice-versa.
There is a relatively recent standard from CommonJS called the Asynchronous Module Definition (AMD). This spec is designed to support modules in browser code via a library that implements a function called define
. AMD modules can also be used in server-side code with some shimming.
Using AMD, this contrived code:
module MyModule (a, b)
import Language.Fay.Prelude
import ModuleA (foo)
import ModuleB (bar)
a = foo
b = bar
could be transformed into JavaScript like this:
define("MyModule", [
"Fay.Prelude",
"ModuleA",
"ModuleB"
], function(Fay, ModuleA, ModuleB) {
var a = Fay.$(function() { return Fay._(ModuleA.foo); });
var b = Fay.$(function() { return Fay._(ModuleB.bar); });
return {
a: a,
b: b
};
});
Putting Fay's Prelude into its own module is one way of sharing that code among multiple emitted modules without duplication. But it would also work just fine to stick with the current method of putting the Prelude code in a scoping function with the compiled code:
(function() {
var True = true;
var False = false;
/* ... */
define("MyModule", ["ModuleA", "ModuleB"], function(ModuleA, ModuleB) {
var a = $(function() { return _(ModuleA.foo); });
var b = $(function() { return _(ModuleB.bar); });
return {
a: a,
b: b
};
});
/* ... */
}());
An advantage to putting Fay's Prelude in its own module is that you could provide a command-line switch or something to leave it out of the emitted code in case someone prefers to load that code separately.
For best possible interoperability, I recommend including a check in the emitted code for the existence of a define
function. If such a function exists you will want to use it. Otherwise you can include a minimal definition. This snippet maps a global define
function to a locally-scoped alias called def
, or supplies a new implementation for def
:
var def = typeof define === "function" ? define : function() {
/* minimal implementation */
};
There are a number of AMD implementations. For this case I suggest either tAMD or almond since both are intended to be minimal. Disclaimer: I wrote tAMD.
Unless I'm mistaken there is no way to generate an expression of the form so[exp]
, o[exp] = exp
, exp[exp] = exp
. Has this not been handled or is there a reason for this that I didn't get? :)
The reason I'm asking is that I'm working on changing the representation of records and would like to be able to map a pattern match on a RecDecl
to the correct properties, such as case r of Rec 1 -> ...
=> rec[rec._fields[0]] === 1
.
We have JsSetProp JsName JsName JsExp
and JsGetProp JsExp JsName
which should in that case be changed to JsSetProp JsExp JsExp JsExp
and JsGetProp JsExp JsExp
, perhaps with helper functions for the simpler old cases.
And add the output/results to the Docs.
:this
is transformed into this
in Language/Fay/Print.hs:jsEncodeName. Replace it by adding a JsThis constructor to JsExp in Language/Fay/Types.hs.
Basically what you did originally, change it back to that.
It would be useful to be able to set properties of JS objects through a function like this, analogous to how foreignFay operates.
For instance:
setInnerHTML :: Foreign f => Fay f -> Fay f
setInnerHTML = `foreignFay "innerHTML" ""
main = do
body <- getBody
setInnerHTML body "foo"
would correspond to writing document.body.innerHTML = "foo";
. There are a lot of more or less obscure browser cases where this might be useful, such as setting cancelBubble for IE<9.
As far as I can tell this isn't implemented yet. Also, is there a way of reading global properties such as when defining getBody
?
How to optimize the following simple function:
sum 0 acc = acc
sum n acc = sum (n - 1) (acc + n)
As Fay outputs presently:
var sum = function ($_a) {
return function ($_b) {
return new $(function () {
var acc = $_b;
if (_($_a) === 0) {
return acc;
}
var acc = $_b;
var n = $_a;
return _(_(sum)(_(n) - _(1)))(_(acc) + _(n));
});
};
};
Evaluation times:
500000500000
492ms
500000500000
457ms
500000500000
454ms
500000500000
467ms
What Fay could output by detecting that the function is tail-recursive:
var sum = function($_a) {
return function ($_b) {
return new $(function(){
while (true) {
var acc = $_b;
if (_($_a) === 0) {
return acc;
}
var acc = $_b;
var n = $_a;
$_a = _(n) - _(1);
$_b = _(acc) + _(n);
}
});
};
};
Evaluation times:
500000500000
137ms
500000500000
137ms
500000500000
129ms
500000500000
124ms
What Fay would ideally output with strictness annotations in order to remove the forcing:
var sum = function($_a) {
return function ($_b) {
return new $(function(){
$_a = _($_a);
$_b = _($_b);
while (true) {
var acc = $_b;
if ($_a === 0) {
return acc;
}
var acc = $_b;
var n = $_a;
$_a = n - 1;
$_b = acc + n;
}
});
};
};
Evaluation times:
500000500000
28ms
500000500000
20ms
500000500000
11ms
500000500000
11ms
This becomes basically native JavaScript speed.
What Fay could do without strictness annotations and simple inlining of the forcer (which is much easier than pervading strictness meta data):
var sum = function($_a) {
return function ($_b) {
return new $(function(){
while (true) {
var acc = $_b;
if ($_a instanceof $?_($_a):$_a === 0) {
return acc;
}
var acc = $_b;
var n = $_a;
$_a = (n instanceof $?_(n):n) - 1;
$_b = (acc instanceof $?_(acc):acc) + (n instanceof $?_(n):n);
}
});
};
};
Evaluation times:
500000500000
66ms
500000500000
56ms
500000500000
56ms
500000500000
57ms
Currently there is:
foreignFay :: Foreign a => String -> FayReturnType -> a
foreignPure :: Foreign a => String -> FayReturnType -> a
foreignValue :: Foreign a => String -> FayReturnType -> a
foreignProp :: Foreign a => String -> FayReturnType -> a
foreignPropFay :: Foreign a => String -> FayReturnType -> a
foreignMethodFay :: Foreign a => String -> FayReturnType -> a
foreignMethod :: Foreign a => String -> FayReturnType -> a
foreignSetProp ::
(Foreign object, Foreign value) =>
String -> object -> value -> Fay ()
By using the type signature of an FFI declaration, we can determine enough information, to have only:
ffi :: Foreign a => String -> FayReturnType -> a
And allow to write arbitrary content:
%1.foo=%2
As described in UHC's JS backend paper. This gives us a fair bit of flexibility.
Hi,
Fay is looking great. I think it would be cool to include a way to show an example of JSON.
I wrote up a quick example here and included how easy it would be to interface with the Aeson library for data serialization.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.