GithubHelp home page GithubHelp logo

elm-native-module's Introduction

Elm native module

This is highly discouraged, and will not be possible in future versions of Elm.

The core Elm libraries need access to JavaScript to expose HTTP in Elm (for example) but this is not a public API. It is not intended for public use. It can change dramatically from release to release. It is not permitted in published packages. And again, in future versions, it will not be accessible.

In summary, it is better to figure out how to structure code such that ports will be a good option. If you cannot achieve what you want after asking around on Slack, the best path may be to circle back around to Elm later when more of the Web Platform is covered to your liking.





What?

This is an example of how to do elm native modules for elm 0.18. It contains a native module with a helloWorld function which receives a name parameter and returns a string with that name appended to the 'Hello ' string and an exclamation at the end.

Why?

There are not too many tutorials about how native modules. One of them is the wiki of take-home but it is not updated for elm 0.17 and up. Also, I have included some tests, so you can learn how to test your own native modules.

How?

Getting Starting

You need to have elm installed, and also elm-test because we are going to write a test for our native module. You can do it with this commands:

$ npm i -g elm
$ npm i -g elm-test

Once you have both installed, we are goint to create a folder and execute elm-test init. That will create two folders, src and tests. Test folder comes with some example elm tests.

.
├── src
└── tests
    ├── Main.elm
    ├── Tests.elm
    └── elm-package.json

You can execute them running elm-test in your command line.

After running elm-test you should see something like this:

first run elm-test

Our next step will be writting our first test. To do that we have to replace the content of tests/Tests.elm with this content:

module Tests exposing (..)

import Test exposing (..)
import Expect

import NativeModule exposing (helloWorld)

all : Test
all =
    describe "Native module test suite"
        [ describe "helloWorld"
            [ test "empty string" <|
                \() ->
                    Expect.equal (helloWorld "") "Hello !"
            ]
        ]

If we try to run elm-test again, an error will show up telling us that the module NativeModule we are trying to import can't be found. That is right... we haven't done it yet!.

Our next step will be create that module, so we have to create a file called NativeModule.elm inside the src folder. (You can replace NativeModule with something more descriptive in your project).

Inside it, we are going write the lines under this:

module NativeModule exposing (helloWorld)

helloWorld: String -> String
helloWorld =
  Native.NativeModule.helloWorld

There, we are creating a module called NativeModule which is exposing a function called helloWorld. That function annotation is telling us that it is going to receive a string as a parameter and it is going to return another string. Also we are telling that its implementation is going to be Native.NativeModule.helloWorld. What is going to happend if we try to run our tests again?

If we do it, we will see an error in the terminal:

ReferenceError: _user$project$Native_NativeModule is not defined

What does it mean? Elm compiler is telling us that it is trying to find a native module with the name _user$project$Native_NativeModule, that it because in Elm all the packages are scoped by the username and the project name where they are stored in github. If you change the content of the line repository in tests/elm-package.json for something like https://github.com/gabrielperales/elm-native-module.git and then you run elm-test again you will see that now it is trying to find _gabrielperales$elm_native_module$Native_NativeModule. Let's going to implement that module!

Create a new file in src/Native called NativeModule.js and write this code there:

var _gabrielperales$elm_native_module$Native_NativeModule = function(){
  function helloWorld(name){
    return "Hello " + name + "!";
  }

  return {
    helloWorld: helloWorld
  };
}();

(In your project, you can again rename NativeModule.js to something more appropriate, but the parent directory must be Native for it to be found, at least with current Elm).

You will also need to modify your elm-package.json file to include the directive:

{
    ...
    "native-modules": true,
    ...
}

The last thing we have to do is import that native module inside our elm module. You only have to type import Native.NativeModule under you module definition.

Your module should end looking like this:

module NativeModule exposing (helloWorld)
{-| Creating our first native module

# helloWorld function
@docs helloWorld
-}
import Native.NativeModule

{-| This function will return 'Hello ' prepended to the name you are passing to it
-}
helloWorld : String -> String
helloWorld =
    Native.NativeModule.helloWorld

Finally, we can run our first test again and it should pass.

Now we can write a new test to check that every thing works as we were expecting.

, test "name" <|
    \() ->
        Expect.equal (helloWorld "Gabi") "Hello Gabi!"

If everything is ok, it also should pass that test.

For functions of more than one argument, you can wrap the function with FN, where N is the number of arguments. This turns the function into one that can be called using Elm conventions, while still being written as a normal Javascript function. For example:

var _gabrielperales$elm_native_module$Native_NativeModule = function(){
  function sum(one, two){
    return one + two;
  }

  return {
    sum: F2(sum)
  };
}();

The functions F2, F3... F9 are provided by Elm.

elm-native-module's People

Contributors

evancz avatar gabrielperales avatar spookylukey avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

elm-native-module's Issues

Add a note about functions with more than one argument

Hi Gabriel! 👋

Thank you for putting this together: It helped me get started with Native modules in Elm 0.18! 🤓

NOTE: This is not really an issue, it’s more like an idea to add to the README, if it looks useful to you. 😉

So, in my case I needed a function that’d take 2 arguments:

// src/Native/Intl.js
var _gurdiga$xo_elm$Native_Intl = function() {
  function formatMoneyAmount(localeName, amount) {
    return "yes";
  }

  return {
    formatMoneyAmount: formatMoneyAmount
  };
}();

and then I tried to call it like this:

Native.Intl.formatMoneyAmount "ro-MD" 42

It compiled, but when run in the browser (through elm-reactor) it crashed the whole Elm app with something like this in the Chrome console:😶

Main.elm:93 Uncaught TypeError: fun(...) is not a function
    at A2 (Main.elm:93)
    at _gurdiga$xo_elm$Utils_Money$format (Main.elm:13254)
    at _gurdiga$xo_elm$Dosar_Actiune_IncheiereIntentare_RezultatIncercareConciliere_PartileNuAjungLaIntelegere_BunuriUrmarite_BunUrmarit$view (Main.elm:15783)
    at itemView (Main.elm:15967)
    at Function.func (Main.elm:1390)
    at A2 (Main.elm:92)
    at Function.foldr [as func] (Main.elm:1116)
    at A3 (Main.elm:98)
    at Function.func (Main.elm:1384)
    at A2 (Main.elm:92)

Then I went on to look how it’s done in the elm-lang/core and found this in Native/Debug.js:

return {
	crash: crash,
	log: F2(log)           // ← See the F2 thingy?
};

Then I looked in my bundle and found F2:

function F2(fun)
{
  function wrapper(a) { return function(b) { return fun(a,b); }; }
  wrapper.arity = 2;
  wrapper.func = fun;
  return wrapper;
}

So my guess after all of this, is that if a native function that we want to use in Elm takes more than one argument, it has to be wrapped into an F<number> thing like that — it seems like further on Elm decides how many arguments to pass it. 🤔 (I found F1 to F9 in my bundle. 🤓)

I’m not sure where would this insight go in the README, but I thought this may be useful for someone! 🤓

Cheers! 🙂

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.