efene

Erlang Factory Lite Brussels 2012

Mariano Guerra

me

efene

fnt

write a template like this

hello ${name}!

compile it

fnt:build(tpls, [{hello, "hello.fnt"}, {hello_name, "helloname.fnt"}]).

fnt (usage)

6> tpls:hello_name([{name, "mariano"}]).
["hello ","mariano","!\n"]

7> OutName = tpls:hello_name([{name, "mariano"}]).
["hello ","mariano","!\n"]

8> io:format(OutName).
hello mariano!
ok

9> OutName1 = tpls:hello_name([]).
["hello ","undefined","!\n"]

10> io:format(OutName1).
hello undefined!
ok

fnt (low level)

10> io:format(fnt:to_erlang(tpls, [{hello, "hello.fnt"}, {hello_name, "helloname.fnt"}, {greet, "greet.fnt"}, {list, "list.fnt"}, {insecure, "insecure.fnt"}])).
-module(tpls).

-export([hello/1, hello_name/1, greet/1, list/1,
         insecure/1]).

hello(Context) -> ["hello world!\n"].

hello_name(Context) ->
    ["hello ", fnt:escape(fnt:get(Context, [name])), "!\n"].

greet(Context) ->
    [case fnt:get(Context, [name]) == "admin" of
       true -> " well hello sir ";
       _ ->
           case fnt:get(Context, [name]) == "root" of
             true -> " you shouldn't be using the root user ";
             _ ->
                 [" welcome ", fnt:escape(fnt:get(Context, [name])),
                  "! "]
           end
     end,
     "\n\n",
     case fnt:get(Context, [name]) == "mariano" of
       true -> "hello mariano, you have superpowers!";
       _ -> ""
     end,
     "\n"].

list(Context) ->
    [fnt:each(fnt:get(Context, [item]),
              fun (Index, Value) ->
                      [" ", fnt:escape(Index), ": ", fnt:escape(Value), "\n"]
              end),
     "\n"].

insecure(Context) ->
    ["this is some content without escaping: ",
     fnt:get(Context, [value]),
     "\nthe same content escaped: ",
     fnt:escape(fnt:get(Context, [value])), "\n"].

qrly

qrly (usage)

1> {ok, Qrly} = qrly_html:parse("../extra/test/test.html").
...

2> Q1 = qrly_html:filter(Qrly, "h1").
[{<<"h1">>,
  [{<<"class">>,<<"first-title asdwordclass">>}],
  [<<"personal">>]},
 {<<"h1">>,
  [{<<"class">>,<<"second-title wordclass">>}],
  [<<"projects">>]},
 {<<"h1">>,
  [{<<"title">>,<<"others">>},
   {<<"class">>,<<"third-title wordclass">>}],
  [<<"others">>]}]

3> Q2 = qrly_html:filter(Qrly, "h1.first-title").
[{<<"h1">>,
  [{<<"class">>,<<"first-title asdwordclass">>}],
  [<<"personal">>]}]

4> io:format("~s~n", [qrly_html:to_string(Q1)]).
<div><h1 class="first-title asdwordclass">personal</h1><h1 class="second-title wordclass">projects</h1><h1 title="others" class="third-title wordclass">others</h1></div>
ok

5> io:format("~s~n", [qrly_html:to_string(Q2)]).
<div><h1 class="first-title asdwordclass">personal</h1></div>
ok

erlang Argentina

pynerl

pynerl (usage)

1> pynerl:eval("t = 1", "t").
1

2> pynerl:eval("t = 1 * 2", "t").
2

3> pynerl:eval("import time;t = time.time()", "t").
1274236802.877999

4> pynerl:eval("import random;t = random.random()", "t").
0.45102117275294684

5> pynerl:eval("t = print('hello erlang or python')", "t").
hello erlang or python
                      none

6> pynerl:call("time", "time", []).
1274236859.510859

7> pynerl:call("random", "random", []).
0.9623136682858975

8> pynerl:eval("t = True", "t").
true

9> pynerl:eval("t = 2.3", "t").
2.3

and more stuff

emesene

rst2html5

why are you showing me all this?

you

so ... what's efene?

history

status

reason of existence

reason of existence (cont.)

In any language design, the total time spent discussing a feature in this list is proportional to two raised to the power of its position.

  1. Semantics

  2. Syntax

  3. Lexical syntax

  4. Lexical syntax of comments

Wadler's Law

reason of existence (cont..)

Syntax is the user interface of programming languages

Simon Peyton-Jones

reason of existence (cont...)

many languages taking erlang concepts

they are willing to reinvent erlang, but not use it :(

reason of existence (cont....)

Any sufficiently complicated concurrent/fault tolerant program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Erlang.

Mariano Guerra's Tenth Rule

show me some code

Hello World (efene)

@public
hello = fn ("Winston Churchill") {
    io.format("Well, hello sir~n")
}

fn (Name) {
    io.format("Hello ~s!~n", [Name])
}

@public
run = fn () {
    hello("Winston Churchill")
    hello("Erlang Factory Lite Brussels")
}

output

➜  fnc hello.fn
Compiling hello.fn

➜  fnc -r hello run
Well, hello sir
Hello Erlang Factory Lite Brussels!

Hello World (erlang)

-module(hello).

-export([hello/1, run/0]).

hello("Winston Churchill") ->
    io:format("Well, hello sir~n");
hello(Name) ->
    io:format("Hello ~s!~n", [Name]).

run() ->
    hello("Winston Churchill"),
    hello("Erlang Factory Lite Brussels").

Hello World (ifene)

@public
hello = fn ("Winston Churchill")
    io.format("Well, hello sir~n")

fn (Name)
    io.format("Hello ~s!~n", [Name])

@public
run = fn ()
    hello("Winston Churchill")
    hello("Erlang Factory Lite Brussels")

what we saw in the examples

temporary variables (erlang)

-module(tempvars).

-export([run/0]).

add(L, N) ->
    add(L, N, []).

add([], _N, Accum) ->
    lists:reverse(Accum);

add([H | T], N, Accum) ->
    add(T, N, [H + N | Accum]).

run() ->
    L = lists:seq(1, 10),

    L1 = add(L, 2),
    L2 = lists:reverse(L1),

    io:format("~p~n", [L2]).

temporary variables (ifene)

add = fn (L, N)
    add(L, N, [])

add = fn ([], _N, Accum)
    lists.reverse(Accum)

fn ([H:T], N, Accum)
    add(T, N, [H + N:Accum])

@public
run = fn ()
    L = 1 .. 10 -> add(2) -> lists.reverse()

    io.format("~p~n", [L])

data types

>>> 1
1

>>> 1.2
1.2

>>> atom
atom

>>> Var = "string"
"string"

>>> Bstr = <["binary string"]>
<["binary string"]>

>>> [1, 2, 3]
[1, 2, 3]

>>> (1, 2, 3)
(1, 2, 3)

>>> [X + 1 for X in 1 .. 10]
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

>>> for X in 1 .. 10 { X + 1 }
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

objects

>>> P = {name: "Mariano", lastname: "Guerra"}
{name: "Mariano", lastname: "Guerra"}

>>> P.name
"Mariano"

>>> P.lastname
"Guerra"

>>> P1 = struct.set_prototype(P)
{name: "Mariano", lastname: "Guerra"}

>>> P1.has(name)
true

>>> P1.has(lastname)
true

>>> P1.has(money)
false

>>> P1.fields()
[name, lastname]

>>> P1.to_plist()
[(name, "Mariano"), (lastname, "Guerra")]

objects (cont)

>>> P2 = P1.name := "Luis Mariano"
{name: "Luis Mariano", lastname: "Guerra"}

>>> P1
{name: "Mariano", lastname: "Guerra"}

>>> P1.format()
[123, "name: \"Mariano\", lastname: \"Guerra\"", 125]

>>> P1.print()
{name: "Mariano", lastname: "Guerra"}
ok

>>> P3 = struct.from_plist([name => "bob", lastname => "sponge"])
{name: "bob", lastname: "sponge"}

>>> ok => 42
(ok, 42)

blocks

@public
run = fn
    L = lst.map(1 .. 10) do (X)
        X * 2

    io.format("~p~n", [L])

output

➜  fnc block.ifn
Compiling block.ifn

➜  fnc -r block run
[2,4,6,8,10,12,14,16,18,20]

meta (erlang)

-module(meta).

-export([erlfun/1]).

-define(ERLNUM, 4).
-define(ERLMACRO(A), A + 1).

erlfun(A) ->
    fun (B) ->
        B * A
    end.

meta (ifene)

@import("meta.erl")

$FnNum = 42
$FnMacro = fn (A)
    A + 1

fnfun = fn (A)
    fn (B)
        B * A

@public
run = fn ()
    io.format("erlang constant ~p, macro ~p, fun ~p~n", [
        $ERLNUM,
        $ERLMACRO(1),
        erlfun(2)(3)])

    io.format("efene constant ~p, macro ~p, fun ~p~n", [
        $FnNum,
        $FnMacro(1),
        fnfun(2)(3)])

more meta

@public
run = fn ()
    L = $[lists.seq(1000, 1010)]
    io.format("~p~n", [L])
-module(moremeta).

-export([run/0]).

run() ->
    L = [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007,
         1008, 1009, 1010],
    io:format("~p~n", [L]).

even more meta

>>> [|A + 1|]
(op, 1, '+', (var, 1, 'A'), (integer, 1, 1))

>>> ast.line(15, [|A + 1|])
(op, 15, '+', (var, 15, 'A'), (integer, 15, 1))

>>> A = [|1|]
(integer, 1, 1)

>>> B = [|2|]
(integer, 1, 2)

>>> Add = [|$(A) + $(B)|]
(op, 1, '+', (integer, 1, 1), (integer, 1, 2))

other features

what I would like efene to become

future

help!

technical stuff

how do I build it?

git clone https://github.com/marianoguerra/efene.git
cd efene
./build.sh

build.sh will

first time usage (REPL)

run

$ bin/fnc -s

write

>>> io.format("Hello world!~n")
Hello world!
ok

>>>

first time usage (compiling)

write (hello.fn)

@public
run = fn () {
    io.format("Hello World!~n")
}

compile

$ fnc hello.fn
Compiling hello.fn

run

$ fnc -r hello run
Hello World!

first time usage (translating)

run

$ fnc -t erl hello.fn

you get

-module(hello).

-export([run/0]).

run() -> io:format("Hello World!~n").

efene in your app

Create a dir for the project, and download rebar

$ mkdir myapp
$ cd myapp
$ wget http://bitbucket.org/basho/rebar/downloads/rebar; chmod u+x rebar

Create a file named rebar.config

{deps, [
  {efene, ".*",
    {git, "git://github.com/marianoguerra/efene.git", "master"}
  },
  {rebar_efene_plugin, ".*",
    {git, "git://github.com/DavidMikeSimon/rebar_efene_plugin.git", "stable"}
  }
]}.

{plugins, [ rebar_efene_plugin ]}.
{plugin_dir, "deps/rebar_efene_plugin/src"}.

efene in your app (cont.)

Create a directory named src, and within it create a myapp.app.src file

{application, myapp, [
  {description, "My first app ever"},
  {vsn, "0.0.1"}
]}.

write an efene file

tell rebar to get the dependencies and compile

$ rebar get-deps
$ rebar compile

thanks!