4

How do the native JavaScript types get written in Elm? The files defining List are surprisingly short and easy to read:

I am curious of how Elm does it's job of proving a type-safe interface to JavaScript. Is the JavaScript written by hand, or is it compiled from another language like Haskell?


Elm's List Type

The key line of Elm seems to be ...

module List exposing
  ( isEmpty, length, reverse, member
  , head, tail, filter, take, drop
  , repeat, (::), append, concat
  )

import Basics exposing (..)
import Maybe
import Maybe exposing ( Maybe(Just,Nothing) )
import Native.List

(::) : a -> List a -> List a
(::) = Native.List.cons

infixr 5 ::

head : List a -> Maybe a
head list =
  case list of
    x :: xs ->
      Just x

    [] ->
      Nothing

tail : List a -> Maybe (List a)
tail list =
  case list of
    x :: xs ->
      Just xs

    [] ->
      Nothing

This tells us that head and tail can be defined within Elm, but (::) operation must be defined with JavaScript. Here are some key lines in JavaScript to have to do with the constructor:

var _elm_lang$core$Native_List = function() {

var Nil = { ctor: '[]' };

function Cons(hd, tl) { return { ctor: '::', _0: hd, _1: tl }; }

...

return {
    Nil: Nil,
    Cons: Cons,

    // etc
};

}();

I am especially curious about the line { ctor: '::', _0: hd, _1: tl }; since the word ctor is the second half of the word functor -- which may mean a functor was written in JavaScript.


These considerations are important because I am considering writing a Tree a and/or QuadTree a type and it might be good to write from inside Elm (e.g. using Records) or to write a module for personal use.

  • one implementation of tree could just have nodes
  • another could distinguish between Left and Right nodes
  • another just has 4 indistinguishable nodes.

Another example could be CircularList but I just want to focus on trees for now.

Community
  • 1
  • 1
john mangual
  • 7,718
  • 13
  • 56
  • 95

2 Answers2

3

I think ctor is short for "constructor" and the List is just implemented as a linked list. Each node is either the empty list ({ ctor: '[]' }) or is made of a value (at _0) and the rest of the list (at _1) put together with the '::' constructor.

Ex the list [1, 2] would be Cons(1, Cons(2, Nil)) which fully expanded would be

{ ctor: '::',
  _0: 1,
  _1: { ctor: '::',
        _0: 2,
        _1: { ctor: '[]' } } }

You can see in the toArray function that it is using xs.ctor !== '[]' to check if it has reached the end of the list, or if it should push the head on to the array and keep going.

function toArray(xs)
{
    var out = [];
    while (xs.ctor !== '[]')
    {
        out.push(xs._0);
        xs = xs._1;
    }
    return out;
}
robertjlooby
  • 7,160
  • 2
  • 33
  • 45
  • a linked list of length 1000 would be very nested. unless they use pointers – john mangual Jun 28 '16 at 00:15
  • I don't think there are any optimizations for large lists. The `Array` type is supposed to be more performant and there was even a proposal to switch the default collection from `List` to `Array` citing performance as the #1 reason https://github.com/elm-lang/elm-plans/issues/13 – robertjlooby Jun 28 '16 at 00:37
  • oh wow is there a trade-off between being immutable and being fast? http://elm-lang.org/blog/announce/0.12.1 Elm is very much a work in progress, I still like it very much – john mangual Jun 28 '16 at 01:36
2

My hunch is that the implementation of List is written in Javascript primarily because its constructors are [] and ::, but Elm doesn't allow for infix operators as constructor names.

For example, the way we use List in Elm means the definition really should be something like this:

type List a = ([]) | (::) a (List a)

That doesn't compile in Elm even though we pattern match on those constructors, so the shortcut of using Javascript to define the implementation is used.

If you write your own custom list implementation with different names, the javascript output is the same as the javascript implementation of List.

type MyList a = MyNil | MyCons a (MyList a)

... yields ...

var _user$project$Temp1467112617033056$MyCons = F2(
  function (a, b) {
    return {ctor: 'MyCons', _0: a, _1: b};
  });
var _user$project$Temp1467112617033056$MyNil = {ctor: 'MyNil'};

Therefore, I don't see any reason why your proposed Tree or QuadTree types would benefit from being written in a Native javascript module. I'd recommend writing them in Elm.

Chad Gilbert
  • 36,115
  • 4
  • 89
  • 97