Open gelisam opened 2 years ago
Another challenge is that while a piece of Haskell code can use hint to evaluate a string to a value of any Typeable type, there will be a single piece of Haskell code which will be responsible for interpreting all the Haskell code found in all the Klister files, each of which has a different Klister type and a corresponding Haskell type.
One solution might be to use polymorphic recursion to instantiate that single piece of Haskell code at just the right type, but @david-christiansen came up with a simpler solution: define a single type, a GADT, capable of encoding a variety of Klister types: function of arbitrary arity over our few primitive types. It is less clear how to deal with datatypes.
data KlisterTypeRep a where
KlisterInteger :: KlisterTypeRep Integer
KlisterString :: KlisterTypeRep String
KlisterArrow :: KlisterTypeRep a -> KlisterTypeRep b -> KlisterTypeRep (a -> b)
Then, using more GADTs and a Parameterized.Pair, we can construct e.g. a value of that type, or a TypeRep
for that type, etc.
It would also be quite useful to add an opaque ffi type to Klister, so that we can define e.g.
(define openFileForReading
(the (-> String (IO (Opaque Handle)))
(ffi "\\filePath -> openFile filePath ReadMode")))
(define hClose
(the (-> (Opaque Handle) (IO Unit))))
without having to specify how a Handle is represented as an algebraic datatype on the Klister side.
Since Klister's type system is intentionally close to Haskell's, it would be great if we could call Haskell code from Klister.
One of the easiest-to-use FFI mechanisms I've seen is Haste's
ffi
function, which takes string containing javascript code and trusts the programmer to make sure the javascript expression does have typea
:Where
FFI a
ensures thatr
has the forma -> b -> ... -> IO c
, that those parameters have types which can be serialized to Javascript using someToJS
typeclass, and that the result type can be deserialized back to Haskell using someFromJS
typeclass.This nicely circumvents the difficulty that Haskell libraries aren't restricting themselves to the subset of Haskell types which Klister supports: the programmer can easily use Haskell's extra features inside the string, as long as it provides a Klister-compatible type at the boundary.
I am thinking of using the hint library (which I maintain) to evaluate a string of Haskell code, but there are some complications:
.ghc.environment.*
file provides the latter two, and hint will look for it in the current-working-directory when klister runs.Read
andShow
instances, but that would be inefficient. The alternative is to use types which haveTypeable
instances, whoseDict
would need to be provided byFromHaskell
andToHaskell
typeclasses in Klister. Except, of course, Klister doesn't have typeclasses yet.String
? HaskellText
? Strict or Lazy?), it is a lot less obvious how to serialize Klister algebraic datatypes. We could define Haskell datatypes with the same names as the Klister datatypes, but Klister and Haskell have different naming conventions, so those names would need to be mangled. Or we could provide them as opaque values, plus some builtin functions for e.g. obtaining the constructor name or extracting a constructor's first argument as another opaque value.ffi_decl : String -> IO ()
, for adding those definitions to some kind of environment which futureffi
calls will see? How do multipleffi
calls making use of the same definitions make sure that theffi_decl
command they depend on is only run once? Perhaps the easiest would be to require the programmer to define a new package and to import their helper functions from there.