Why Bucklescript?

The elm language was modeled on OCaml and Haskell, most of the syntax comes directly over. ^.^

It can do everything Elm can do, and a great deal more (it is a full language that is older than Erlang).

And nope, not whitespace dependent, Elm has that because of some of its haskall’isms, OCaml does not have that, it is fully expression based, right down to the module level. In Ocaml you can do this if you want:

let myFunc i =
i+42

And it works as you expect, where in Elm that same code (sans the ‘let’ since Elm uses haskell’isms) will give you an error unless you indent the i+42 part. :wink:

Here is the Counter example from Elm put in to OCaml (via my OAK-TEA library, which emulates the Elm API precisely): bucklescript-testing/src/main_counter.ml at master · OvermindDL1/bucklescript-testing · GitHub

Breaking it down:

open Tea.App
open Tea.Html

This is the same as this in Elixir:

import Tea.App
import Tea.Html

It just imports the ‘usage’ of what is in the modules into the namespace so you don’t need to prefix (you can ‘open’ in smaller scopes too if you do not want to open to the entire file, like in Elixir too).

type msg =
  | Increment
  | Decrement
  | Reset
  | Set of int

This is similar to Elm (except in Elm the first Increment cannot have a |, have to put the = instead, which bugs me), it just defines a type, msg in this case, that can have 4 custom names, one of which, the Set one, can also hold an integer. Standard ADT, think of it like this in Elixir (and in fact there is at least one Elixir library that simplifies this for you too):

:Increment # Is the same as the `Increment` in OCaml
:Decrement # Is the same as the `Decrement` in OCaml
:Reset # is the same as the `Reset` in OCaml
{:Set, 42} # is the same as `Set 42` in OCaml

The equivalent of the usual tagged tuples of {:ok, data} and {:error, reason} in Elixir/Erlang is the Result.t type in OCaml, it gives you an Ok data and Error reason variant constructors, as another example, etc… Very generic variant data container, same as tagged tuples in Erlang/Elixir (in fact that is how they would be implemented in an OCaml->Elixir compiler).

let update model = function
  | Increment -> model + 1
  | Decrement -> model - 1
  | Reset -> 0
  | Set v -> v

This defines a function names update, that takes a model and another argument (the function keyword is like a function-level match context, just code simplification, it would be the same as doing let update model message = match message with of the let line, the rest is the same) to match on, would be like this in Elixir:

def update(model, message) do
  case message do
    :Increment -> model + 1
    :Decrement -> model - 1
    :Reset -> 0
    {:set, v} -> v
  end
end

And it is fully type-safe (trying to return an integer, for example, would fail, telling you that it expects an int instead), thus catching lots of programming bugs.

The next set of code:

let view_button title msg =
  button
    [ onClick msg
    ]
    [ text title
    ]

let view model =
  div
    []
    [ span
        [ style "text-weight" "bold" ]
        [ text (string_of_int model) ]
    ; br []
    ; view_button "Increment" Increment
    ; br []
    ; view_button "Decrement" Decrement
    ; br []
    ; view_button "Set to 42" (Set 42)
    ; br []
    ; if model <> 0 then view_button "Reset" Reset else noNode
    ]

This is less OCaml and more Elm, I duplicated the Elm API to a T(EA), it is just building a DOM in javascript via a virtual-dom, same as in Elm, this is the only [] that you find here if you notice. It would be the same as this in Elixir:

def view_button(title, msg) do
  button([
      onClick(msg)
    ], [
      text(title)
    ])
end

def view(model) do
  div([
    ], [
      span([
          style("text-weight", "bold")
        ], [
          text(Integer.to_string(model)
        ]),
      , br([])
      , view_button("Increment", :Increment)
      , br([])
      , view_button("Decrement", :Decrement)
      , br([])
      , view_button("Set to 42", {:Set, 42})
      , br([])
      , if model != 0 do view_button("Reset", :Reset) else noNode()
    ])
end

It is just DOM building, near everything takes lists of attributes and lists of elements, except it looks worse in Elixir (could simplify some things so a br with no arguments implies a [] so br() would work, but eh…).

The last bit of code:

let main =
  beginnerProgram {
    model = 4;
    update;
    view;
  }

Just calls my beginnerProgram thing in my OAK-TEA library, you pass in the callbacks (a default model of 4, the update function, and the view function, you could do those as update = update; and such too, but if the name is already the same it is optional, and yes ; is the list separation operator, same as , in Elixir). The ‘main’ variable is just what I call it, you can call it whatever you want, just elm calls in main and in elm it is forced, where it is optional here, but I still call it the same for the examples. This would be something like this in Elixir:

def main() do
  beginnerProgram(%{
    model: 4,
    update: &update/2,
    view: &view/1
    })
end

But yes, compared to Elm the language is actually well tested and battle-hardened over decades, not changing every major version, still being developed and worked on (a lot actually, more so than erlang), and the semicolons/brackets all depend on what API you are working with, by default it has substantially less than Elm, but with my OAK-TEA API it duplicates that part of Elm too. ^.^

9 Likes