Skip to content

Fay Status Update September 2013: ZuriHac, typeclasses, haskell suite, and strictness wrappers

Adam Bergmark edited this page Sep 4, 2013 · 7 revisions

ZuriHac is now over (awesome event!). We had some people helping out and I'm very happy with the results. All the code is available on the annotate branch, and will be merged into master soon.

Lets jump into the news!

Annotated AST

I spent most of the time converting Fay from the regular haskell-src-exts AST into the Annotated AST. Very tedious and unstimulating work, but I knew it had to be done eventually. This was a huge refactoring, but in itself it doesn't add anything.

haskell-names

Roman Cheplyaka has done wonderful work in getting parts of the haskell-suite ready. haskell-names makes it very simple to perform name resolution on an AST, and the result is that AST annotated with name information (where the identifier is defined, what type of identifier it is, etc).

I performed the migration to haskell-names this weekend. There are a few issues which means my branch has a few regressions. But the home grown name resolution code we had is very ugly and buggy and it's nice that this has been abstracted for us. I'm sure some of the related bugs were automatically resolved now.

Regressions:

  • RecordWildCards and NamedFieldPuns are not supported haskell-suite/haskell-names#36
  • Recursive positions are resolved as local haskell-suite/haskell-names#35. ones = 1 : ones resolves ones as a local binding, but it is top level. Edit: This has been fixed in haskell-names already!
  • RebindableSyntax not available. The annotated resolution in haskell-names means we cannot simply ask "Where is >>= bound?" if the operator isn't in the AST. But haskell-names also has open name resolution which means we can modify the AST and haskell-names will propagate name changes throughout the AST for us.

Big thanks to Roman for helping out with the migration!

haskell-packages

haskell-names depends on haskell-packages, but you don't have to use any of it if you don't need package management. Moving to haskell-packages means we would get a more stable way of handling packages. With haskell-packages by itself we have access to a package database where we can store compilation artifacts which means we don't have to recompile all dependencies when compiling a new module. With haskell-packages and haskell-names we can create and store interface file containing export information for packages.

This hasn't been done yet. To use these features haskell-packages needs a patched version of Cabal. It's available in a haskell-suite repo but IIRC it won't be released until Cabal 1.20 (no release date announced). But we can still use haskell-names!

Type classes and Typechecking

Typeclasses is the biggest thing that's missing from Fay. Tom Lokhorst and I looked into this. There is an implementation of the paper Typing Haskell in Haskell on Hackage (it works!), and Shayan Najd has a repository based on this, but it seems this package has a long way to go since it isn't integrated with haskell-names. We'd like to annotate the HSE AST with typing information to have access to the class constraints we need to implement type classes. I've contacted him so we can see how we can proceed.

A THIH implementation would give us Haskell98 (probably easily extended to Haskell2010) support. We don't get RankNTypes et al. but I think it'd be enough to make Fay programming a lot better.

If we want full GHC support (which the haskell-suite might want) it might be reasonable to take the same approach as they did and implement a constraint solver. Either a new one or try to re-use GHC's implementation. This is probably a really big project though so I'm not sure how likely it is to happen.

If all fails, not all hope is lost! We can still support some type classes without type information.

Take Show as an example:

class Show a where
  show :: a -> String
instance Show X where
  show _ = "X"
f x = show x

Here the constraint is on a which is in a contravariant position. Data types are reperesented as constructors so we can dynamically dispatch based on that value. The generated code for a call to show can then be

f = function (x) { return x.show(x) }

and the implementation of the instance method would be

X.prototype.show = function (_) { return "X" }

But not all contravariants are okay, someClassMethod :: a => Maybe a does not work since we in general can't access the value we want to dispatch based on.

But we can support several type classes. Eq is (==) :: a -> a -> a (doesn't matter which of the arguments we dispatch on), Ord works. Enum works except for toEnum :: Int -> a. Even Functor works! But not monads and applicatives because of return/pure.

For other type classes users can define them using dictionaries

type ReadDict a = a -> String
class Read a where
  read :: ReadDict a -> String -> a

This is of course not that nice, but it may come in handy.

For the short term we'll probably go with the latter approach since it could also be used to to optimize the code generated from a typed ADT.

JavaScript->Fay communication

A new module system was released in 0.16 which now produces modules at the top level instead of them all being defined in a closure.

The goal is to generate a strict wrapper around the exported declarations that can be used to create libraries meant to be used from JavaScript. Without this it is very clunky to do so since you have to manually call the serialization/deserialization functions and to manual forcing.

For a declaration f x y = g x y we can generate something like Strict.x = Fay$$fayToJs(["automatic"], f) which will generate a javascript function with two parameters whose return value will be serialized to a JavaScript format. automatic works with functions thanks to this pull request by Sebastiaan Visser.

This is something that no other to-Javascript-compilers I know of do (or even focus on), JavaScript-sugared languages such as TypeScript and CoffeeScript excluded of course. Once you pick your compiler you need to put it as the entry point of your application meaning you can use JS libraries but not the other way around. The main use case is for projects with large JavaScript code bases that would like to write Haskell but can't afford to do a one shot migration. Write some libraries in Fay and then just call them from JS as if they were JS modules! You could even write NPM modules and distribute, if that floats your boat.

Upcoming Fay talks

Thanks for reading! For comments you can head over to this thread on r/haskell