Elm has no genericity mechanisms for structures by now, like Haskell typeclasses, nor instance arguments.

Elm gives a parser error if you try to use type variables for structures like (m a) in a function.

Instead you have a few predefined, compiler bound, type variable categories.

(Examples tested with Elm version 0.18)

Check it here.

Elm's predefined functions are at Basics, but there are additional default imports.

Elm has predefined type variable categories that have implicit functionality. You specify a type var. category by means of a prefix. Check the compiler function categorizeVar

type variables with "number" as prefix form a category that corresponds to Haskell's Num typeclass (a ring structure), defines (+), (-), (*), abs , negate , adding (^) and clamp to the pack. Check the link documentation for available instances.

as prefix form a category that corresponds to Haskell's typeclass (a ring structure), defines (+), (-), (*), , , adding (^) and to the pack. Check the link documentation for available instances. "comparable" type var category corresponds to Haskell's Ord functionality

type var category corresponds to Haskell's functionality "appendable" type var category corresponds to Haskell's Semigroup with (++) as right associative infix operator for append

To extend the functionality with new functions based on the predefined ones for the category, you only have to categorize the type variable of the parameter using the category prefix.

Elm's Int has the integer division as double slash (//) and mod as (%), and rem as rem. (/) is only for Floats

But there is a catch with (//). It does not return the integer part when dividing negatives. It behaves like haskell's quot truncating towards zero !! So it does not correspond to the euclidean division definition because it doesn't comply the rule stating that the remainder should be non‑negative.

import Html as H import List as L -- (//) behaves like `quot`, not like `div` intDblSlashIsQuot : Int -> Int -> Bool intDblSlashIsQuot denom num = abs (num // denom) == abs (-num // denom) checkProperty = intDblSlashIsQuot 3 main = H.text <| toString <| L.map checkProperty [7, -2, 4, -8]

Elm's Float (JavaScript's IEEE754 Number) corresponds to Haskell's Double

(JavaScript's IEEE754 Number) corresponds to Haskell's Parameters with the type category number allow Int and Float as actual parameter types.

allow Int and Float as actual parameter types. Elm's data types use the clause keyword type . Type aliases are defined with "type alias".

. Type aliases are defined with "type alias". Elm has a special uninhabited type Never to be used as a Task error type parameter for tasks that cannot fail.

Elm's Maybe corresponds to Haskell's Maybe

corresponds to Haskell's Elm's Result has an homomorphic corresponding in Haskell's Either

has an homomorphic corresponding in Haskell's Squared bracket literals [1,3,5] denote Elm's Lists, not Arrays.

-- zips are missing but easy done zip : List a -> List b -> List (a,b) zip = List.map2 (,) zip3 = List.map3 (,,) zip4 = List.map4 (,,,)

Elm's random access sequence Array is immutable , not like JS ones. A Haskell corresponding is Vector from Data.Vector.Unboxed

Elm's linear access sequence List

Elm's mapping Dict structure (dictionary) requires comparable key types (ordered domains). This includes Int, Float, Time, Char, String, and tuples or lists of comparable types. Its corresponding in Haskell is Data.Map.Strict where stored values are forcedly evaluated.

Elm's Set structure as a special case of Dict where the elements are the keys, has the same restriction.

Elm's Task structure is for effect actions that may fail. It implements Haskell's MonadError throwing errors with fail that skips the subsequent actions until the onError monadic catcher is evaluated, and its type (Task err a) would correspond to IO lifted to a MonadError compliant transformer, like (ExceptT err IO a) .

Haskell range syntax is not supported in Elm. Specific functions are used: List.range. But you can define a dot-dot operator (..)

import Html as H import List (..) = List.range infix 9 .. main = H.text <| toString (1..7)

A Haskell style stepped range for Ints defining enumFromThenTo.

import Html as H import List as L import Debug as D import Char as Ch -- tailrec intEnumFromThenToTR : List Int -> Int -> Int -> Int -> List Int intEnumFromThenToTR acc ini nxt top = if ini == nxt then D.log "next must be different than initial" [] else if {- ini beyond top -} ini /= top && compare ini top /= compare ini nxt then L.reverse acc else intEnumFromThenToTR (ini :: acc) nxt (nxt + nxt - ini) top intEnumFromThenTo = intEnumFromThenToTR [] -- using this seems to slow result yield v0 = intEnumFromThenTo 9 7 1 -- [9,7,5,3,1] v1 = intEnumFromThenToTR [] 9 7 1 -- not as elegant but maybe faster -- refactored intEnumFromThenTo2 : Int -> Int -> Int -> List Int intEnumFromThenTo2 ini0 nxt0 top = if ini0 == nxt0 then D.log "next must be different than initial" [] else let step = nxt0 - ini0 go acc ini nxt = if {- ini beyond top -} ini /= top && compare ini top /= compare ini nxt then L.reverse acc else go (ini :: acc) nxt (nxt + step) in go [] ini0 nxt0 v2 = intEnumFromThenTo2 9 7 1 charEnumFromThenTo: Char -> Char -> Char -> List Char charEnumFromThenTo ini0 nxt0 top = if ini0 == nxt0 then D.log "next must be different than initial" [] else L.map Ch.fromCode <| intEnumFromThenTo2 (Ch.toCode ini0) (Ch.toCode nxt0) (Ch.toCode top) v3 = charEnumFromThenTo 'Y' 'W' 'A' main = H.text <| toString (v2, v3)

Using partial application seems to yield result later in Elm's try-online vs. specifying all function arguments at once. Check it also with the program at the page bottom.

Strict evaluation.

Single pattern definitions.

No where clauses.

Elm's Prelude is described here.

Elm's API search is here.

Haskell's id is called identity in Elm.

Haskell's const is called always in Elm.

Haskell's flip , curry and uncurry are named equally in Elm.

Haskell's function application Data.Function.($) corresponds to Elm's (<|)

Haskell's reverse application Data.Function.(&) corresponds to Elm's (|>)

Haskell's right to left composition Control.Category.(<<<) corresponds to Elm's (<<)

Haskell's left to right composition Control.Category.(>>>) corresponds to Elm's (>>)

Haskell's show corresponds to Elm's toString

Haskell's Data.Tuple fst , snd are defined in Elm's Basics, but they are named first , second in Elm's Tuple

Tuple constructors are defined for arity (<= 9), but the Tuple module advises against it use, proposing records instead.

import Html as H v = (,,,,,,,,) 1 2 3 4 5 6 7 8 9 -- `_Tuple9` Ok, `_Tuple10` undefined main = H.text (toString v)

Functions arity has a top of 9 arguments

Functor: Haskell Data.Functor.fmap corresponds to Elm structures' map function

Monad: Haskell's bind (>>=) corresponds to Elm structures' andThen flipped.

Applicative: With Elm structures' (map2, .., map5) you have the functionality that corresponds to Haskell's Control.Applicative (liftA2, ...) to combine the results of a string of actions as subsequent parameters.

For Task's map{N}, action side effects are serialized with andThen (Haskell's bind) so they rather correspond to Control.Monad.liftM{N} instead of Control.Applicative.liftA{N}.

>> The Elm compiler is able to do tail-call elimination on a function when any of the branches are a direct self-recursive call. See Tail-call elimination.

Lists

-- from Elm's core library List foldl : (a -> b -> b) -> b -> List a -> b foldl func acc list = case list of [] -> acc x :: xs -> foldl func (func x acc) xs -- tail recursive foldr : (a -> b -> b) -> b -> List a -> b foldr = Native.List.foldr -- via JS list conversion toArray(xs) -- map is based on foldr, so elements are traversed right to left map : (a -> b) -> List a -> List b map f xs = foldr (\x acc -> f x :: acc) [] xs -- Task.sequence traverses left-to-right a list of Tasks giving a Task that returns the List of results. It uses map2 (like Applicative.liftA2 but serializing the side effects: Control.Monad.liftM2) to concatenate its results.

Simulation of a Haskell do block, nesting subsequent monadic functions to have all lambda argument variables in scope. Try it in Elm's try online:

import Html as H exposing (Html) import Maybe as M -- Maybe.andThen : (a -> Maybe b) -> Maybe a -> Maybe b -- simulation of a Haskell "do" block monadic : Maybe Int monadic = Just 1 |> M.andThen (\x -> Just 2 |> M.andThen (\y -> let y2 = y * y -- a `do block` let in Just (x + y2) |> M.andThen (\z -> Just (x + y2 + z) -- y2 still in scope ))) -- rewriting it with (>>=) (>>=) m f = M.andThen f m infixl 1 >>= monadic2 : Maybe Int monadic2 = Just 1 >>= (\x -> Just 2 >>= (\y -> let y2 = y * y -- a `do block` let in Just (x + y2) >>= (\z -> Just (x + y2 + z) -- y2 still in scope ))) main : Html String main = H.text <| toString (monadic, monadic2)

See also elm-lang/lazy. Lazyness memoisation is advised against. Have a look at the link's "Pitfalls" section regarding memory usage for memoization of lazy expressions and parameters.

import Html as H -- lazyness without memoization type Lazy a = Lazy (() -> a) force : Lazy a -> a force (Lazy f) = f () -------------- -- use: maybeOrElse : Maybe a -> Lazy a -> a maybeOrElse mbX lzY = case mbX of Just x -> x Nothing -> force lzY v = maybeOrElse Nothing <| Lazy (\_ -> sqrt 2) main = H.text <| toString v

import Html as H exposing (Html) -- appendables (Semigroup) have (++) as infixr for append (See Basics) main : Html msg main = H.text <| "Hello " ++ "cruel world!"

Web programs without effects follow the so called beginnerProgram structure as explained here.

Html.beginnerProgram : { model : model, view : model -> Html msg, update : msg -> model -> model } -> Program Never model msg

main = Html.beginnerProgram { model = initialModel, view = view, update = update }

Html.program : { init : (model, Cmd msg), update : msg -> model -> (model, Cmd msg), subscriptions : model -> Sub msg, view : model -> Html msg } -> Program Never model msg

Effect actions (tasks) are not evaluated directly but by sending a command to an Effect manager that will be handled differently based on their protocol processing state.

Effect managers support protocol processing by handling its own message queue (selfMsg queue), and may send messages to the app main loop through the appMsg queue parameter of a Router object assigned to them.

You can interact with them sending commands and subscriptions specific to the effect manager which finally will send messages routed to your app main loop, and handled by the update function.

See the guide on Effects and the Platform Router

-- from the Platform module: {-| Task: Represents asynchronous effects that may fail. It is useful for stuff like HTTP. -} type Task err ok = Task {-| Router: An effect manager has access to a “router” that routes messages between the main app and your individual effect manager. -} type Router appMsg selfMsg = Router {-| sendToApp: The effects manager module will be able to send the router a message for the main loop of your app. This message will be handled by the overall `update` function, just like events from `Html`. -} sendToApp : Router appMsg a -> appMsg -> Task err () {-| sendToSelf: The effects manager module will be able to send the router a message for its own message queue. This message will be routed to the `onSelfMsg` function, where you can update the state of your effect manager as necessary. -} sendToSelf : Router a selfMsg -> selfMsg -> Task err ()

The (Cmd msg) and (Sub msg) types are the types of requests for commands or subscriptions sent to the effect managers (see below) that may send app messages to be processed by the update function.

Each effect manager exposes user entry points to build such requests.

-- from Platform.Cmd {-| Cmd msg: the type of commands to Elm's effects Every Cmd specifies (1) which effects you need access to and (2) the type of messages that will come back into your application. -} type Cmd msg -- from Platform.Sub {-| Sub msg: the type of commands to Elm's effects Every Sub specifies (1) which effects you need access to and (2) the type of messages that will come back into your application. -}

Elm has modules qualified by the effect keyword as effect managers. They handle Msgs from/to the run time system.

As example, the Websocket Effect Manager skeleton, expliciting which queue the message types are related:

effect module WebSocket where { command = MyCmd, subscription = MySub } exposing ( send , listen , keepAlive ) ... -- MyCmd and MySub are parameterized by an appMsg type to encode app. message constructors. type MyCmd appMsg = Send String String type MySub appMsg = Listen String (String -> appMsg) | KeepAlive String -- | user entry calls to build command or subscription requests send : String -> String -> Cmd appMsg send url txt = command (Send url txt) listen : String -> (String -> appMsg) -> Sub appMsg listen url tagger = subscription (Listen url tagger) -- ^ tagger: appMsg constructor to wrap the received String with keepAlive : String -> Sub appMsg keepAlive url = subscription (KeepAlive url) -- | request processing automaton (input -> state -> state) callback onEffects : Platform.Router appMsg SelfMsg -> List (MyCmd appMsg) -- cmd requests -> List (MySub appMsg) -- subscription requests -> State appMsg -> Task Never (State appMsg) onEffects router cmds subs state = ... -- | State: The msg param in the State type is the appMsg of the type of the current subscriptions type alias State msg = { sockets : SocketsDict , queues : QueuesDict , subs : SubsDict msg } -- | selfMsgs (type changed from Msg to SelfMsg to avoid confusion) type SelfMsg = GoodOpen String WS.WebSocket | BadOpen String | Receive String String | Die String -- | selfMsgs processing automaton callback onSelfMsg : Platform.Router appMsg SelfMsg -> SelfMsg -> State appMsg -> Task Never (State appMsg) onSelfMsg router selfMsg state = ... -- | cmdMap called by Platform.Cmd.map cmdMap : (a -> b) -> MyCmd a -> MyCmd b -- | subMap called by Platform.Sub.map subMap : (a -> b) -> MySub a -> MySub b

See WebSockets client program from the Elm's guide.

Other effect managers: Time (subscription only), Random (command only), other at core library or Effects pkg list

The type (Task err a) represents an action that may fail.

You may run a Task with the commands perform or attempt, that send a message to the appMsg queue upon finalisation, through the Task effect manager (See the module's source).

Within Effect managers, automaton callbacks are Tasks. You can also run tasks asynchronously through Process.spawn and abort them with Process.kill.

The structure Task implements Haskell's Monad and MonadError functionality as well, for computations that can throw errors whose type is determined by the type of the monad, here (Task err), where throwError bypasses subsequent actions until catchError is found.

succeed -> Haskell Monad's return implementation

-> Haskell Monad's implementation andThen -> Haskell Monad's flipped bind impl.

-> Haskell Monad's flipped impl. fail -> Haskell MonadError's throwError

-> Haskell MonadError's onError -> Haskell MonadError's catchError

-> Haskell MonadError's map -> Haskell Functor's fmap

-> Haskell Functor's map2 -> Haskell Control.Monad's liftM2

-> Haskell Control.Monad's map3 -> Haskell Control.Monad's liftM3

-> Haskell Control.Monad's sequence -> Haskell Control.Monad sequence for a List container

effect module Task where { command = MyCmd } exposing ( Task -- the type , perform, attempt -- cmds to run tasks , succeed, andThen -- return, bind , map, map2, map3, map4, map5 -- functor, liftM{N} , sequence -- sequence a list of tasks , fail, onError, mapError -- throwError, catchError ) {-| Command to perform asynchronously a Task that cannot fail (err ~ Never) and send a msg to the appMsg queue -} perform : (a -> msg) -> Task Never a -> Cmd msg perform toMessage task = command (Perform (map toMessage task)) {-| Command to attempt asynchronously a task that might fail and send a msg chosen by the resultToMessage function -} attempt : (Result err a -> msg) -> Task err a -> Cmd msg attempt resultToMessage task = command (Perform ( task |> andThen (succeed << resultToMessage << Ok) -- Ok is a constructor of type Result |> onError (succeed << resultToMessage << Err) -- Err is a constructor of type Result )) {-| Simple task generator that succeeds immediately when run. A Haskell's Monad `return` implementation. -} succeed : a -> Task err a {-| Tasks chaining. A flipped version of Haskell's Monad `bind` -} andThen : (a -> Task x b) -> Task x a -> Task x b {-| fail gives a task that fails immediately when run. fail "file not found" : Task String a fail: Monadic exception thrower, MonadError's throwError -} fail : err -> Task err a {- onError: Monadic exceptions catcher, MonadError's catchError -} onError : (x -> Task y a) -> Task x a -> Task y a {- mapError: to wrap the error type into a wider union one with a constructor -} mapError : (x -> y) -> Task x a -> Task y a {- Functor map -} map : (a -> b) -> Task x a -> Task x b map f taskA = taskA |> andThen (\a -> succeed (f a)) {- Results combination of serialized tasks: Haskell's liftM2 -} map2 : (a -> b -> c) -> Task x a -> Task x b -> Task x c map2 f2 taskA taskB = taskA |> andThen (\a -> taskB |> andThen (\b -> succeed (f2 a b) )) ... {-| form a Task from a list of tasks returning the list of results. -} sequence : List (Task x a) -> Task x (List a) sequence tasks = case tasks of [] -> succeed [] task :: remainingTasks -> map2 (::) task (sequence remainingTasks)

module Task_Extra exposing (..) import Task exposing (Task, succeed, andThen, map2) -- lazyness without memoization type Lazy a = Lazy (() -> a) force : Lazy a -> a force (Lazy f) = f () -- let's call traverse what in Haskell would be mapM or traverseM traverse : (a -> Task err b) -> List a -> Task err (List b) traverse f li = case li of [] -> succeed [] x :: xs -> map2 (::) (f x) (traverse f xs) -- side effects only traverse_ : (a -> Task err ()) -> List a -> Task err () traverse_ f li = case li of [] -> succeed () x :: xs -> f x |> andThen (\_ -> traverse_ f xs) -- Haskell's monadic forM for = flip traverse for_ = flip traverse_ when : Bool -> Lazy (Task err ()) -> Task err () when cond lzTask = if cond then force lzTask else succeed () whenM : Lazy (Task err Bool) -> Lazy (Task err ()) -> Task err () whenM lzBoolTask lzTask = force lzBoolTask |> andThen (\cond -> when cond lzTask) -- replicate with Index replicate : Int -> (Int -> Task err a) -> Task err (List a) replicate n f = if n <= 0 then succeed [] else let go i = -- i: 0..n if (i < n) then map2 (::) (f i) <| go (i+1) else succeed [] in go 0 -- side effects only replicate_ : Int -> (Int -> Task err ()) -> Task err () replicate_ n f = if n <= 0 then succeed () else let go i action = -- i: 0..n if (i < n) then go (i+1) (f i) -- tail recursive else succeed () in go 0 (succeed ())

Check the function application templates and run this program in Elm's try-online.

import Html as H exposing (Html) import Time as TM exposing (Time) import Platform.Cmd import Platform.Sub import Task as TS exposing (Task) import List as L import Debug as D -- (>>=) for Tasks (>>=) m f = TS.andThen f m infixl 1 >>= type Lazy a = Lazy (() -> a) force : Lazy a -> a force (Lazy f) = f () type MyErr = MyErr String type Msg = Start | Elapsed (Time, Time) | MsgErr String type alias Model = {elapsed: Result String (Time, Time)} intEnumFromThenToTR : List Int -> Int -> Int -> Int -> List Int intEnumFromThenToTR acc ini nxt top = if ini == nxt then D.log "next must be different than initial" [] else if {- ini beyond top -} ini /= top && compare ini top /= compare ini nxt then L.reverse acc else intEnumFromThenToTR (ini :: acc) nxt (nxt + nxt - ini) top intEnumFromThenTo = intEnumFromThenToTR [] -- using partial application seems to slow execution v0 = intEnumFromThenTo 1003 1001 1 v1 = intEnumFromThenToTR [] 1003 1001 1 -- maybe faster -- evaluate the deferred lzA parameter N times iterateM_ : Int -> Lazy a -> Task MyErr () iterateM_ n lzA = case compare n 0 of LT -> TS.fail <| MyErr "iterateM_: n must be positive" EQ -> TS.succeed () GT -> let -- tail recursive, evaluating the action as parameter go m action = if m == 0 then TS.succeed () else go (m-1) (TS.succeed (force lzA) >>= (\_ -> TS.succeed ())) in go n (TS.succeed ()) -- time N evaluations of lzA timeItN : Int -> Lazy a -> Task MyErr Time timeItN n lzA = TM.now >>= (\ tmIni -> iterateM_ n lzA >>= (\ _ -> TM.now >>= (\ tmFinal -> TS.succeed (tmFinal - tmIni) ))) timeV : List Int -> Task MyErr Time timeV v = timeItN 1000 <| Lazy (\_ -> L.sum v) targetsTimes : Task MyErr (Time, Time) targetsTimes = timeV v0 >>= (\ t1 -> timeV v1 >>= (\ t2 -> TS.succeed (t1, t2) )) timesResultToMsg : Result MyErr (Time, Time) -> Msg timesResultToMsg res = case res of Ok v -> Elapsed v Err (MyErr str) -> MsgErr str view : Model -> Html msg view model = case model.elapsed of Err str -> H.div [] [H.text <| "Error: " ++ str] Ok (t1, t2) -> -- arrange (t1, t2) in a List to map units once let res = L.map TM.inMilliseconds [t1, t2] in H.div [] [H.text <| toString res] update : Msg -> Model -> (Model, Cmd Msg) update msg mdl = case msg of Start -> (mdl, TS.attempt timesResultToMsg targetsTimes) Elapsed tm -> ({ mdl | elapsed = Ok tm}, Cmd.none) MsgErr str -> ({ mdl | elapsed = Err str}, Cmd.none) init : (Model, Cmd Msg) init = ({elapsed = Ok (0, 0)}, TS.perform (\_ -> Start) (TS.succeed ())) -- send Start msg main = H.program { init = init, update = update, view = view, subscriptions = (\_ -> Sub.none)}

PureScript offers alternatives for designing Web User Interfaces much nearer to the language power of Haskell and much lighter weight than GHCJS (no GHC RTS emulation).

See Purescript client-side MVC frameworks