Closed cdsmith closed 11 years ago
That boilerplate is all for vanilla GHC, e.g. in GHCi and such. In the IDE I add the NoImplicitPrelude by default, so this kind of manipulation is not a big deal. I think we can totally add that manipulation to the type checking part so that invocations to fay
that use the GHC type checking get the points above that you'd want. It makes sense for stand-alone use, such as in your case.
For the module declarations, http://stackoverflow.com/questions/12703151/make-ghc-accept-main-module-with-a-main-function-that-isnt-io
So I actually wonder if it might not be a good idea to just rename Language.Fay.Prelude to Prelude. This would resolve both of the prelude-related issues, without doing any source code manipulation at all. It may get in the way, though, if some future build work includes using cabal... but those aren't really new problems; using cabal would already require figuring out what to do with packages (e.g., basically all of them) that explicitly depend on base
. Cabal currently has no good answer for wanting to change out the implementation of dependencies behind a library's back.
In any case, passing '-XNoImplicitPrelude' to ghc from typecheck
in the interim is an easy and obvious step, right?
In any case, passing '-XNoImplicitPrelude' to ghc from typecheck in the interim is an easy and obvious step, right?
Sure, passing that and also maybe inserting an import
statement so that the user doesn't have to type it.
So I actually wonder if it might not be a good idea to just rename Language.Fay.Prelude to Prelude. This would resolve both of the prelude-related issues, without doing any source code manipulation at all. It may get in the way, though, if some future build work includes using cabal... but those aren't really new problems; using cabal would already require figuring out what to do with packages (e.g., basically all of them) that explicitly depend on base. Cabal currently has no good answer for wanting to change out the implementation of dependencies behind a library's back.
I'm happy to do that, in fact I was considering it to be a natural thing to do. I'm just not sure that GHC can trivially be told “this Prelude is Fay's, not yours”. When using it from GHCi I don't know how this would be done. Of course if you're just using fay
for everything, this can be circumvented a bit. I specifically hack in Emacs with GHCi and generate the JS with fay --no-ghc
, so I'd still need that workflow to work.
You should be able to do, e.g., ghci -hide-package base -package fay
. Would that be sufficient? Or do you mean you want one GHCi instance that can be used with both server and client side code? A good example of a package that already works this way is haskell98
-hide-package base
sounds like it could work… :-)
Or do you mean you want one GHCi instance that can be used with both server and client side code?
I do. But… I suppose I can run separate sessions…
So the benefit of this would be that you can forgoe NoImplicitPrelude
and replace base
with fay
and it will automatically import the Prelude
module from fay
? Not bad.
Yes, that's the idea. Though, on further thought, exporting a Prelude
module from fay
itself is going to be very unpopular with anyone (like myself, eventually) who wants to use the Fay compiler as a library, since every module in that code will then have to use NoImplicitPrelude
and PackageImports
to import the correct Prelude
. The right way to do it is probably to provide a separate package called something like fay-base
with the pieces intended to be imported by Fay code.
Hmm. Yeah. Sounds cleaner. We can do that once I add Cabal support.
I'm still thinking about this. We have:
fay-base
is just a hack for running ghc
and ghci
fay-base
becomes a way of marking whether a package is Fay or Haskell.I suppose that disadvantage could go away, if the approach were something like writing fay-base:Prelude
as a module of re-exports from base, and having the fay compiler replace it with whatever Fay needs. So Fay itself would never read fay-base:Prelude
; it's only there as an adapter to get GHC to type-check against the right set of symbols? Maybe...
I may be missing something (regarding the cabal flags), but isn't it enough to have two separate cabal sections for compiling haskell and fay?
I tried to fix the module issue as well, but I don't understand the error I'm getting. Typechecking with ghc without using the fay executable works as expected, otherwise I get a linker(?) issue.
I put the change on the dummymain
branch.
Note that you have to cabal install to test this so that DummyMain is available.
[~/repos/fay/fay (dummymain)] > ./cabal-dev/bin/fay tests/Bool.hs
"-fno-code -package fay -XNoImplicitPrelude -main-is Language.Fay.DummyMain tests/Bool.hs -i. -i/Users/adam/repos/fay/fay/cabal-dev//share/fay-0.9.2.0/src"
fay: Warning: the following files would be used as linker inputs, but linking is not being done: -main-is Language.Fay.DummyMain
tests/Bool.hs:5:1:
Couldn't match expected type `GHC.Types.IO t0'
with actual type `Fay ()'
In the expression: main
When checking the type of the function `main'
[~/repos/fay/fay (dummymain)] > ghc -fno-code -package fay -XNoImplicitPrelude -main-is Language.Fay.DummyMain tests/Bool.hs -i. -i/Users/adam/repos/fay/fay/cabal-dev//share/fay-0.9.2.0/src
[1 of 1] Compiling Main ( tests/Bool.hs, nothing )
I think that one of Fay's killer apps is that you can write code that can be easily moved between client and server, or run on both. This is what attracted us to Fay, and so I'd like to weigh in on this from a user's standpoint:
IMHO, the ideal goal is to keep the source code identical for server and client where possible, but have different implementations possible. This could apply:
For commonly used Haskell libraries. I want to write:
import Data.List
This would compile as normal with GHC, but would have a Fay-specific implementation. Perhaps there would be a module Language.Fay.Data.List, and fay would know to rewrite the imports to have a Language.Fay in front. Once tuple unpacking is in, the implementation could more or less be copied and pasted from Haskell. A partial implementation would also be acceptable. Over time, it could be filled out and optimized by using native JS functions. If a Fay implementation didn't exist, I could supply my own as Fay.Data.List.
For user-written modules. I'd like to write
import MyModule
MyModule could be pure haskell which also compiles as fay, or it could have two alternate implementations (eg; a 3D graphics library for OpenGL and WebGL). I could supply the Fay implementation as Fay.MyModule, and the fay compiler would automatically rewrite the import if Fay.MyModule existed.
I don't understand the internals of the Fay or GHC compiler - but it seems to me that all this could be accomplished entirely by having the fay compiler rewrite the import statements early in the compilation process...? This would also mean that existing code with explicit Language.Fay.Prelude imports would not be broken.
James, the way this would work using fay-base
is as follows.
There would be a package fay-base
that exports modules called Prelude
, Data.List
, etc. These packages are also exported by base
. In the default GHC package database, base
is (of course) exposed by default, and fay-base
is hidden. When building for Fay, you'd specify Cabal or GHC options to arrange things the other way around.
My hope would be that Fay would gain support for -XCPP
, so that fay-base
would just re-export types and classes from base
when built with GHC, much like haskell98
does today, and code built against fay-base
and code built against base
would get along just fine when used in GHC and GHCi. When building with fay
, fay-base
would implement everything in pure code and Fay's FFI.
As someone building code to work on both Fay and native Haskell, what this means is that you need to either separate your Fay code and your explicitly GHC-targeted Haskell code (built against base
) into separate packages, or else use -XPackageImports
(and extend Fay to understand that flag). Code built against fay-base
would be compatible with BOTH environments, but would also be more limited in the functionality available to it. Code compiled against base
is, of course, GHC-only. If you wanted modules to have different implementations depending on whether they are built with Fay or GHC, then I think your options are to use -XCPP
(again, if it were implemented), or to use Cabal flags with different source directories.
Compatibility with existing code is easy. It's just a matter of adding a deprecated module to fay-base
that's just module Language.Fay.Prelude (module Prelude) where {}
The positive here is that it works with Haskell name resolution and avoids ugly module name rewriting magic. The disadvantage is needing -XPackageImports
or separate packages to combine Fay and Haskell code.
So I encountered a bug (referenced to this issue) due to this exact problem, I have to conditionally compile the Maybe
type when compiling with Fay but GHC won't accept another Maybe definition. It seems the fay-base
solution solves this mess.
Let's have fay-base
export Prelude
and FFI
. This could also export Data.List
, etc. CPP stuff can come after. Nice work coming up with this, @cdsmith!
Currently there are a lot of sources of boilerplate in Fay modules. For example:
It would be nice to remove all that boilerplate.
Context: I'm hoping to use Fay for an education project with new programmers, and every line of "just copy this, and don't worry about what it means yet" makes Fay less suitable for that purpose. I could copy the user's code into an existing boilerplate module, but this is both unsafe, and limits flexibility (for example, if user code is pasted after a boilerplate module decl, then it's too late for advanced users to add their own LANGUAGE pragmas)