Tosh Tosh - 2 months ago 16x
Node.js Question

How to create worker with elm 0.17

For elm 0.16, I just defined some ports
(that are just

of data) without main function and
to process data back and forth.
It is just for data processing called from nodejs (not from browser),
so I do not depend on

Now elm 0.17 has
instead of
I cannot figure out how to do the same...

Can anybody give me an simplest example to do data
processing via ports exposed by
with elm 0.17?

Here is my simplest example with Elm 0.16...

elm 0.16 code:

module Main where
import Signal
import String exposing (isEmpty, reverse)

-- input ports
port jsToElm : Signal String

-- output ports
port elmToJs : Signal String
port elmToJs
= String.reverse jsToElm

javascript (es6) code:

/** main */
const Elm = loadElm('./index.js') // custom function to eval Elm code
const app = Elm.worker( Elm.Main, { jsToElm: ''})


app.ports.elmToJs.subscribe(( txt ) => {
console.log( txt )



There is now a package that gives you the ability to create a worker. See lukewestby/worker

Original Answer

I was able to get a working example by creating an Html.App program. I can't find any way around the requirement of having a main function that results in a program that includes a view function, and I'm not alone in that confusion.

My example here is browser based, hopefully this fits back into your node-based version; I just haven't used the node version before.


port module Main exposing (..)

import Json.Decode
import Json.Encode
import Html exposing (..)
import Html.App

import String exposing (isEmpty, reverse)

-- input ports
port jsToElm : (String -> msg) -> Sub msg

-- output ports
port elmToJs : String -> Cmd msg

type alias Flags = { jsToElm : String }

main =
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions

type alias Model = { text : String }

init : Flags -> (Model, Cmd Msg)
init flags =
  (Model flags.jsToElm, Cmd.none)

type Msg
  = Reverse String

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Reverse str ->
      let reversed = reverse str
      in ({ model | text = reversed }, elmToJs reversed)

view : Model -> Html.Html Msg
view model =
  text <| "text is: " ++ model.text

subscriptions : Model -> Sub Msg
subscriptions _ =
  jsToElm Reverse


<script type="text/javascript" src="Main.js"></script>
<script type="text/javascript">
var app = Elm.Main.fullscreen({
  jsToElm: 'first'

window.setTimeout(function() {
}, 1);

app.ports.elmToJs.subscribe(function ( txt ) {    
    console.log( txt )

A couple takeaways:

  1. As you can see, the simple mapping of Signals is gone and there is much more boilerplate for such a small example. The idea is that you set up your subscription to the port, then send a Cmd inside the update function when you want to send information back to Javascript.
  2. This whole Cmd/Sub thing requires a Program and the only way to do that (that I could find) was to include [Html.App]. I have a hunch that in the future, the core Router type can be used to make a "headless" worker, but it seems like for now we're stuck with requiring the Html.App program and having to render a view.
  3. You'll notice the setTimeout call when sending "test" to the port in javascript. I'm not sure why this kludge is necessary but others have seen it before as well