Seven languages in seven weeks (notes on Erlang)

1 篇文章 0 订阅

Erlang:


built for concurrency, functional language, no threading, lightweight processes, distributed language passing, reliability: "let it crash", hot-swap code


your programs are going to be built entirely out of functions, with no objects anywhere

those functions will usually return the same values, given the same inputs

those functions will not usually have side effects, meaning they will not modify program state

you will only be able to assign any variable once


 You’ll compile a file with c(filename). (you need the period at the end). You can break out of the console, or a loop, with Control+C. Let’s get started.


 a String is really a List

1> [72, 97, 32, 72, 97, 32, 72, 97].
"Ha Ha Ha"

 In Erlang, a symbol is called an atom and begins with a lowercase character.

 Lists [ ] are heterogeneous and can be any length. You can assign them to variables, just as you would a primitive.
Tuples { } are fixed-length heterogeneous lists

3>  Person = {person, {name, "Agent Smith"}, {profession, "Killing programs"}}.
4> {person, {name, Name}, {profession, Profession}} = Person.
5> Name.
"Agent Smith"

List pattern matching is similar to Prolog’s:

28> [Head | Tail] = [1, 2, 3].
[1,2,3]
29> Head.
1
30> Tail.
[2,3]

 Erlang lets you pack several pieces of data into one byte quite easily. To do these two things, you need two operations: pack and unpack. In Erlang, a bitmap works just like other types of collections.

 Like Ruby, Erlang typing is dynamic.

 The file contains code for a module, and you have to compile it to run it. After you’ve compiled a file, it produces a .beam executable. The .beam compiled module will run in a virtual machine called the beam 


module & function:

-module(basic).
-export([mirror/1]).
mirror(Anything) -> Anything.

compile:

4> c(basic).
{ok,basic}

run:

6> basic:mirror(smiling_mug).
smiling_mug
6> basic:mirror(1).
1

recursive function:

-module(yet_again).
-export([another_factorial/1]).
-export([another_fib/1]).
another_factorial(0) -> 1;
another_factorial(N) -> N * another_factorial(N-1).
another_fib(0) -> 1;
another_fib(1) -> 1;
another_fib(N) -> another_fib(N-1) + another_fib(N-2).



constrol structures:

case:

1> Animal = "dog".
2> case Animal of
2> "dog" -> underdog;
2> "cat" -> thundercat
2> end.
underdog

As with Prolog, you can use the underscore (_) to match anything

3> case Animal of
3> "elephant" -> dumbo;
3> _ -> something_else
3> end.
something_else

if:

9> if
9> X > 0 -> positive;
9> X < 0 -> negative;
9> true -> zero
9> end.

In Erlang, you can assign arbitrary functions to variables and pass them around like any other data types.

anonymous functions:

16> Negate = fun(I) -> -I end.
#Fun<erl_eval.6.13229925>
17> Negate(1).
-1
18> Negate(-1).
1

the keyword "fun" defines an anonymous function. we can easily invoke the underlying function, just by specifying an argument list. (no return type, because of dynamic typing)

 

use functions to manage lists: ForEach, filter, map, foldl, foldr, ...

1> Numbers = [1, 2, 3, 4].
[1,2,3,4]
2> lists:foreach(fun(Number) -> io:format("~p~n", [Number]) end, Numbers).
1
2
3
4
ok

the 2nd line equals to the following:

3> Print = fun(X) -> io:format("~p~n", [X]) end.
8> lists:foreach(Print, Numbers).

The map function works like Ruby’s collect , passing each value of a list to a function and building a list with the results. Like lists:foreach , lists:map takes a function and a list.

10> lists:map(fun(X) -> X + 1 end, Numbers).
[2,3,4,5]

Defining map is really easy:
map(F, [H|T]) -> [F(H) | map(F, T)];
map(F, []) -> [].

Filter:

11> Small = fun(X) -> X < 3 end.
14> lists:filter(Small, Numbers).

lists:all, lists:any, lists:takewhile, lists:dropwhile

Foldl:

29> lists:foldl(fun(X, Sum) -> X + Sum end, 0, Numbers).

or

32> Adder = fun(ListItem, SumSoFar) -> ListItem + SumSoFar end.
#Fun<erl_eval.12.113037538>
33> InitialSum = 0.
0
34> lists:foldl(Adder, InitialSum, Numbers).
10


List construction

-module(double).
-export([double_all/1]).
double_all([]) -> [];
double_all([First|Rest]) -> [First + First|double_all(Rest)].

List comprehension

old-fashioned way:

1> Fibs = [1, 1, 2, 3, 5].
[1,1,2,3,5]
2> Double = fun(X) -> X * 2 end.
#Fun<erl_eval.6.13229925>
3> lists:map(Double, Fibs).
[2,2,4,6,10]

list comprehension:

4> [Double(X) || X <- Fibs].
5> [X * 2 || X <- [1, 1, 2, 3, 5]].

map(F, L) -> [ F(X) || X <- L].

another example:

7> Cart = [{pencil, 4, 0.25}, {pen, 1, 1.20}, {paper, 2, 0.20}].
10> Cat = [{Product, Price} || {Product, _, Price} <- Cart].
11> DiscountedCat = [{Product, Price / 2} || {Product, Price} <- Cat].
[{pencil,0.125},{pen,0.6},{paper,0.1}]

The full form can be even more powerful:
• A list comprehension takes the form of [Expression || Clause1, Clause2, ..., ClauseN].
• List comprehensions can have an arbitrary number of clauses.
• The clauses can be generators or filters.
• A filter can be a boolean expression or a function returning a boolean.
• A generator, of the form Match <-List , matches a pattern on the left to the elements of a list on the right.

22> [X || X <- [1, 2, 3, 4], X < 4, X > 1].
[2, 3]
23> [{X, Y} || X <- [1, 2, 3, 4], X < 3, Y <- [5, 6]].
[{1,5},{1,6},{2,5},{2,6}]


basic concurrency primitives:

(1) sending a message (!)

(2) spawning a process (spawn)

(3) receiving a message (receive)

a basic receive loop

-module(translate).
-export([loop/0]).
loop() ->
    receive
        "casa" -> 
            io:format("house~n"), 
            loop();
        
        "blanca" -> 
            io:format("white~n"), 
            loop();
        
        _ -> 
            io:format("I don't understand.~n"), 
            loop()
    end.

compile, spawn and send messages

1> c(translate).
{ok,translate}
2> Pid = spawn(fun translate:loop/0).
<0.38.0>
3> Pid ! "casa".
"house"
"casa"
4> Pid ! "blanca".
"white"
"blanca"
5> Pid ! "loco".
"I don't understand."
"loco"

Synchronous Messaging

-module(translate_service).
-export([loop/0, translate/2]).

loop() ->
    receive
        {From, "casa" } ->
            From ! "house" ,
            loop();
        {From, "blanca" } ->
            From ! "white" ,
            loop();
        {From, _} ->
            From ! "I don't understand." ,
        loop()
    end.

translate(To, Word) ->
    To ! {self(), Word},
    receive
        Translation -> Translation
    end.

1> c(translate_service).
{ok,translate_service}
2> Translator = spawn(fun translate_service:loop/0).
<0.38.0>
3> translate_service:translate(Translator, "blanca").
"white"
4> translate_service:translate(Translator, "casa").
"house"


Linking processes:

-module(roulette).
-export([loop/0]).

% send a number, 1-6
loop() ->
    receive
        3 -> io:format("bang.~n"), exit({roulette,die,at,erlang:time()});
        _ -> io:format("click~n"), loop()
end.

1> c(roulette).
{ok,roulette}
2> Gun = spawn(fun roulette:loop/0).
<0.38.0>
3> Gun ! 1.
"click"
1
4> Gun ! 3.
"bang"
3
5> Gun ! 4.
4
6> Gun ! 1.
1

7> erlang:is_process_alive(Gun).
false

Monitor:

-module(coroner).
-export([loop/0]).

loop() ->
    process_flag(trap_exit, true), 
    receive
        {monitor, Process} -> 
            link(Process), 
            io:format("Monitoring process.~n"),
            loop();
            
        {'EXIT', From, Reason} -> 
            io:format("The shooter ~p died with reason ~p.", [From, Reason]),
 			io:format("Start another one.~n"), 
            loop()
        end.

before anything else, the program must register the process as one that will trap exits (process_flag), otherwise you won't receive EXIT messages.

This code links the coroner process to any process with a PID of "Process". Now, if the monitored process should die, it will send an exit to this moniter

execution:

1> c(roulette).
{ok,roulette}
2> c(coroner).
{ok,coroner}
3> Revolver=spawn(fun roulette:loop/0).
<0.43.0>
4> Coroner=spawn(fun coroner:loop/0).
<0.45.0>
5> Coroner ! {monitor, Revolver}.
Monitoring process.
{monitor,<0.43.0>}
6> Revolver ! 1.
click
1
7> Revolver ! 3.
bang.
3
The shooter <0.43.0> died with reason
{roulette,die,at,{8,48,1}}. Start another one.



better solution:

-module(doctor).
-export([loop/0]).
loop() ->
    process_flag(trap_exit, true), 
    receive
        new ->
            io:format("Creating and monitoring process.~n"),
            register(revolver, spawn_link(fun roulette:loop/0)),
            loop();
        {'EXIT', From, Reason} -> 
            io:format("The shooter ~p died with reason ~p.", [From, Reason]),
			io:format(" Restarting. ~n"),
            self() ! new, 
            loop()
        end.

Working from the inside out, we spawn a process with spawn_link. That
version of spawn will link the processes so the doctor will get notified
whenever a roulette process dies. We then register the PID, associating
it with the revolver atom. Now, users can send messages to this process
by using revolver ! message. We no longer need the PID. The EXIT match
block is also smarter.

execution:

2> c(doctor).
{ok,doctor}
3> Doc = spawn(fun doctor:loop/0).
<0.43.0>
4> revolver ! 1.
** exception error: bad argument
in operator !/2
called as revolver !1

As expected, we have not created the process yet, so we get an error.
Now, we’ll create and register one:

5> Doc ! new.
Creating and monitoring process.
new
6> revolver ! 1.
click
1
7> revolver ! 3.
bang.
3
The shooter <0.47.0> died with reason {roulette,die,at,{8,53,40}}.
Restarting.
Creating and monitoring process.
8> revolver ! 4.
click
4

You don’t see much error handling here at all. When something crashes, we just start a new one. It’s relatively simple to build monitors that watch each other.


Core Strengths: Erlang is all about concurrency and fault tolerance, from the inside out.







***********

programming paradigm:

1. imperative

2. functional, e.g. Erlang (its sequential subset)

3. object-oriented

4. logic, e.g. Prolog

***********

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值