Here's a question on using the Unison language.
I wanted to play around with defining APIs for data sources/sinks/feeds etc, but I hit a wall. I don't know how I should define an API in unison without tying down the underlying datatype used to implement it.
As a example, consider the following bit of Haskell.
-- let's ignore for the purposes of this discussion whether this is a wise definition of a stream...
class StreamT s where
get :: s a -> (a, s a)
I could instantiate that for a type data Stream a = Cons a (Stream a)
(The fact I've used laziness here is not important to the question at hand). If my code uses StreamT
, then I've avoided coupling to the particular choice of datatype Stream
, and I could swap it out for another one later.
How should I go about this kind of thing in Unison? Here are the two encodings I've thought of.
data StreamT s = StreamT (s a -> (a, s a))
do_stuff_1 : StreamT s -> s a -> (a ->{e} b) ->{e} ()
This is typeclasses without the sugar (and handy method search/dispatch and coherence checks).
ability StreamT s where -- except probably we don't support abilities with
-- type params, let alone higher kinded ones?
get : s a ->{ StreamT s } (a, s a)
stream_handler : Effect (StreamT Stream) t -> t
do_stuff_2 : s a -> (a ->{e} b) ->{e, StreamT s} ()
This is using abilities to delay binding to the specific type's methods.
Could we imagine the language supporting idris-style interfaces one day? (i.e. typeclasses without the coherence checking.)
The answer was: use the direct encoding (or the ability one if it fits); and maybe one day unison will have something similar to typeclasses (but note this).