Objective: to understand how an ML-ish-to-imperative/OO code generator (such as —in this example— the PureScript-to-JavaScript transpiler) represents functional type-classes (FP's approximate use-case equivalent to OO's interfaces).
src/Mini/ShowCls.purs — we recreate the well-known Show
type-class ("interface"),
adding a default instance ("implementation") for String
:
module Mini.ShowCls where import Prelude class MyShow a where showMe :: a -> String instance myShowStr :: MyShow String where showMe s = "\"" <> s <> "\""
src/Mini/ShowImpl.purs — a custom Email
new-type of String
and
adding its own instance of all ShowMe
methods (just showMe
, as per above.)
module Mini.ShowImpl where import Mini.ShowCls newtype Email = At String instance showMail :: MyShow Email where showMe (At addr) = showMe addr
src/Main.purs — user code importing both:
module Main where import Mini.ShowCls import Mini.ShowImpl import Prelude import Control.Monad.Eff import Control.Monad.Eff.Console main :: forall e. Eff (console :: CONSOLE) Unit main = let mail :: Email mail = At "baz@foo.bar" shown = showMe mail in do log shown
output/Main/index.js, imports/exports stripped:
var main = (function () { var shown = Mini_ShowCls.showMe(Mini_ShowImpl.showMail)("baz@foo.bar"); return Control_Monad_Eff_Console.log(shown); })();
At
new-type data constructor call has been erased, just the underlying string remains —
as is proper for all single-constructor, single-arg (ie. all) new-types.MyShow
type-class' showMe
method gets turned into 2 calls:
output/Mini/ShowImpl/index.js
var At = function (x) { return x; }; var showMail = new Mini_ShowCls.MyShow(function (v) { return Mini_ShowCls.showMe(Mini_ShowCls.myShowStr)(v); });
At
new-type constructor of Email
is still being generated, even though its usage was erased (see above).
Not sure why, there's probably a good reason.showMail
instance that the transpiler was smart enough to reference explicitly in Main above.Mini_ShowCls.MyShow(..)
, passing
presumably all concrete "implementing" functions that comprise this instance as constructor arguments.showMe
exported function.showMe
instance for String
, the compiler figured out (just like above in Main)
exactly what underlying concrete function implementation (myShowStr
) to pass on to the embedded showMe
call.output/Mini/ShowCls/index.js
var MyShow = function (showMe) { this.showMe = showMe; }; var showMe = function (dict) { return dict.showMe; }; var myShowStr = new MyShow(function (s) { return "\"" + (s + "\""); });
MyShow
type-class turns into a simple constructor of a representation object with
named methods, initialized in order from the same-named arguments.showMe
calls work, and this function seems utterly superfluous, but probably
this way makes it more elegant to code-gen? Well, we haven't touched type-variables and such here at all.MyShow
instance for String
s (namely, myShowStr
) is being exported,
show-casing the creation of an instance's representation object via the MyCode
constructor with the concrete function
implementation passed in.