Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
####### # # E-scripts on Erlang. # # Note 1: use the eev command (defined in eev.el) and the # ee alias (in my .zshrc) to execute parts of this file. # Executing this file as a whole makes no sense. # An introduction to eev can be found here: # # (find-eev-quick-intro) # http://angg.twu.net/eev-intros/find-eev-quick-intro.html # # Note 2: be VERY careful and make sure you understand what # you're doing. # # Note 3: If you use a shell other than zsh things like |& # and the for loops may not work. # # Note 4: I always run as root. # # Note 5: some parts are too old and don't work anymore. Some # never worked. # # Note 6: the definitions for the find-xxxfile commands are on my # .emacs. # # Note 7: if you see a strange command check my .zshrc -- it may # be defined there as a function or an alias. # # Note 8: the sections without dates are always older than the # sections with dates. # # This file is at <http://angg.twu.net/e/erlang.e> # or at <http://angg.twu.net/e/erlang.e.html>. # See also <http://angg.twu.net/emacs.html>, # <http://angg.twu.net/.emacs[.html]>, # <http://angg.twu.net/.zshrc[.html]>, # <http://angg.twu.net/escripts.html>, # and <http://angg.twu.net/>. # ####### # «.debian» (to "debian") # «.learnyousomeerlang» (to "learnyousomeerlang") # (find-es "elixir") https://en.wikipedia.org/wiki/Elixir_(programming_language) https://stackoverflow.com/questions/30749094/multiline-comment-in-elixir https://en.wikipedia.org/wiki/Erlang_(programming_language) https://stackoverflow.com/questions/25322269/why-is-there-no-multi-line-commenting-support-in-erlang ##### # # debian # 2022dec15 # ##### # «debian» (to ".debian") # (find-zsh "installeddebs | sort | grep erlang") # (find-zsh "availabledebs | sort | grep erlang") # (find-sh "grep-aptavail -P erlang") # (find-status "erlang") # (find-vldifile "erlang.list") # (find-udfile "erlang/") # (find-status "erlang-base") # (find-vldifile "erlang-base.list") # (find-udfile "erlang-base/") # (find-status "erlang-doc") # (find-vldifile "erlang-doc.list") # (find-udfile "erlang-doc/") # (find-status "erlang-examples") # (find-vldifile "erlang-examples.list") # (find-udfile "erlang-examples/") # (find-status "erlang-manpages") # (find-vldifile "erlang-manpages.list") # (find-udfile "erlang-manpages/") # (find-status "erlang-src") # (find-vldifile "erlang-src.list") # (find-udfile "erlang-src/") * (eepitch-shell) * (eepitch-kill) * (eepitch-shell) apti erlang erlang-manpages erlang-doc apti erlang erlang-manpages erlang-src erlang-mode erlang-examples erlang # (find-status "erlang-examples") # (find-vldifile "erlang-examples.list") # (find-udfile "erlang-examples/") # (find-fline "/usr/lib/erlang/lib/gs-1.5.11/examples/src/") # (find-efunction 'erlang-shell-display) # (find-status "erlang-mode") # (find-vldifile "erlang-mode.list") # (find-udfile "erlang-mode/") # (find-fline "/usr/share/emacs/site-lisp/erlang/") # (find-sitelispfile "erlang/erlang.el" "defun inferior-erlang") (require 'erlang) (require 'erlang-start) (require 'erlang-unit) # (find-status "erlang-gs") # (find-vldifile "erlang-gs.list") # (find-udfile "erlang-gs/") # (find-telegachat "-1002188044240#1211" "asdf install elixir latest") asdf install elixir latest asdf asdf install elixir latest asdf global elixir latest ##### # # learnyousomeerlang # 2024sep15 # ##### # «learnyousomeerlang» (to ".learnyousomeerlang") # (find-status "erlang") # (find-vldifile "erlang.list") # (find-udfile "erlang/") # (find-status "erlang-manpages") # (find-vldifile "erlang-manpages.list") # (find-udfile "erlang-manpages/") # http://learnyousomeerlang.com # http://learnyousomeerlang.com/starting-out-for-real * (eepitch-shell) * (eepitch-kill) * (eepitch-shell) erl * (eepitch-erl) * (eepitch-kill) * (eepitch-erl) 2 + 15. % 17 49 * 100. % 4900 1892 - 1472. % 420 5 / 2. % 2 5 div 2. % 1 5 rem 2. % 1 (50 * 100) - 4999. % 1 -(50 * 100 - 4999). % -1 -50 * (100 - 4999). % 244950 2#101010. % 42 8#0677. % 447 16#AE. % 174 One. % * 1: variable 'One' is unbound One = 1. % 1 Un = Uno = One = 1. % 1 Two = One + One. % 2 Two = 2. % 2 Two = Two + 1. % ** exception error: no match of right hand side value 3 two = 2. % ** exception error: no match of right hand side value 2 47 = 45 + 2. % 47 47 = 45 + 3. % ** exception error: no match of right hand side value 48 _ = 14+3. % 17 _. % * 1: variable '_' is unbound Unlike any other kind of variable, it won't ever store any value. Totally useless for now, but you'll know it exists when we need it. Note: If you're testing in the shell and save the wrong value to a variable, it is possible to 'erase' that variable by using the function f(Variable).. If you wish to clear all variable names, do f().. These functions are there only to help you when testing and only work in the shell. When writing real programs, we won't be able to destroy values that way. Being able to do it only in the shell makes sense if you acknowledge Erlang being usable in industrial scenarios: it is wholly possible to have a shell being active for years without interruption... Let's bet that the variable X would be used more than once in that time period. * (eepitch-erl) * (eepitch-kill) * (eepitch-erl) atom. % atom atoms_rule. % atoms_rule atoms_rule@erlang. % atoms_rule@erlang 'Atoms can be cheated!'. % 'Atoms can be cheated!' a.tom. % 'a.tom' atom = 'atom'. % atom true and false. % false false or true. % true true xor false. % true not false. % true not (true and true). % false 5 =:= 5. % true 1 =:= 0. % false 1 =/= 0. % true 5 =:= 5.0. % false 5 == 5.0. % true 5 /= 5.0. % false 1 < 2. % true 1 < 1. % false 1 >= 1. % true 1 =< 1. % true 5 + llama. % ** exception error: bad argument in an arithmetic expression 5 =:= true. % false 0 == false. % false 1 < false. % true X = 10, Y = 4. % 4 Point = {X,Y}. % {10,4} Point = {4,5}. % {4,5} {X,Y} = Point. % {4,5} X. % 4 {X,_} = Point. % {4,5} {_,_} = {4,5}. % {4,5} {_,_} = {4,5,6}. % ** exception error: no match of right hand side value {4,5,6} Temperature = 23.213. % 23.213 PreciseTemperature = {celsius, 23.213}. % {celsius,23.213} {kelvin, T} = PreciseTemperature. % ** exception error: no match of right hand side value {celsius,23.213} {point, {X,Y}}. % {point,{4,5}} [1, 2, 3, {numbers,[4,5,6]}, 5.34, atom]. % [1,2,3,{numbers,[4,5,6]},5.34,atom] [97, 98, 99]. % "abc" [97,98,99,4,5,6]. % [97,98,99,4,5,6] [233]. % "é" [1,2,3] ++ [4,5]. % [1,2,3,4,5] [1,2,3,4,5] -- [1,2,3]. % [4,5] [2,4,2] -- [2,4]. % [2] [2,4,2] -- [2,4,2]. % [] [1,2,3] -- [1,2] -- [3]. % [3] [1,2,3] -- [1,2] -- [2]. % [2,3] hd([1,2,3,4]). % 1 tl([1,2,3,4]). % [2,3,4] List = [2,3,4]. % [2,3,4] NewList = [1|List]. % [1,2,3,4] [Head|Tail] = NewList. % [1,2,3,4] Head. % 1 Tail. % [2,3,4] [NewHead|NewTail] = Tail. % [2,3,4] NewHead. % 2 [1 | []]. % [1] [2 | [1 | []]]. % [2,1] [3 | [2 | [1 | []] ] ]. % [3,2,1] [2*N || N <- [1,2,3,4]]. % [2,4,6,8] [X || X <- [1,2,3,4,5,6,7,8,9,10], X rem 2 =:= 0]. % [2,4,6,8,10] RestaurantMenu = [{steak, 5.99}, {beer, 3.99}, {poutine, 3.50}, {kitten, 20.99}, {water, 0.00}]. % [{steak,5.99}, {beer,3.99}, {poutine,3.5}, {kitten,20.99}, {water,0.0}] [{Item, Price*1.07} || {Item, Price} <- RestaurantMenu, Price >= 3, Price =< 10]. % [{steak,6.409300000000001},{beer,4.2693},{poutine,3.745}] Of course, the decimals aren't rounded in a readable manner, but you get the point. The recipe for list comprehensions in Erlang is therefore NewList = [Expression || Pattern <- List, Condition1, Condition2, ... ConditionN]. The part Pattern <- List is named a Generator expression. You can have more than one! 5> [X+Y || X <- [1,2], Y <- [2,3]]. [3,4,4,5] This runs the operations 1+2, 1+3, 2+2, 2+3. So if you want to make the list comprehension recipe more generic, you get: NewList = [Expression || GeneratorExp1, GeneratorExp2, ..., GeneratorExpN, Condition1, Condition2, ... ConditionN]. Note that the generator expressions coupled with pattern matching also act as a filter: 6> Weather = [{toronto, rain}, {montreal, storms}, {london, fog}, 6> {paris, sun}, {boston, fog}, {vancouver, snow}]. [{toronto,rain}, {montreal,storms}, {london,fog}, {paris,sun}, {boston,fog}, {vancouver,snow}] 7> FoggyPlaces = [X || {X, fog} <- Weather]. [london,boston] If an element of the list 'Weather' doesn't match the {X, fog} pattern, it's simply ignored in the list comprehension whereas the = operator would have thrown an exception. There is one more basic data type left for us to see for now. It is a surprising feature that makes interpreting binary data easy as pie. Speedometer with values in binary Bit Syntax! Most languages have support for manipulating data such as numbers, atoms, tuples, lists, records and/or structs, etc. Most of them also only have very raw facilities to manipulate binary data. Erlang goes out of its way to provide useful abstractions when dealing with binary values with pattern matching taken to the next level. It makes dealing with raw binary data fun and easy (no, really), which was necessary for the telecom applications it was created to help with. Bit manipulation has a unique syntax and idioms that may look kind of weird at first, but if you know how bits and bytes generally work, this should make sense to you. You may want to skip the rest of this chapter otherwise. Bit syntax encloses binary data between << and >>, splits it in readable segments, and each segment is separated by a comma. A segment is a sequence of bits of a binary (not necessarily on a byte boundary, although this is the default behaviour). Say we want to store an orange pixel of true color (24 bits). If you've ever checked colors in Photoshop or in a CSS style sheet for the web, you know the hexadecimal notation has the format #RRGGBB. A tint of orange is #F09A29 in that notation, which could be expanded in Erlang to: 1> Color = 16#F09A29. 15768105 2> Pixel = <<Color:24>>. <<240,154,41>> This basically says "Put the binary values of #F09A29 on 24 bits of space (Red on 8 bits, Green on 8 bits and Blue also on 8 bits) in the variable Pixel." The value can later be taken to be written to a file. This doesn't look like much, but once written to a file, what you'd get by opening it in a text editor would be a bunch of unreadable characters. When you read back from the file, Erlang would interpret the binary into the nice <<240,151,41>> format again! What's more interesting is the ability to pattern match with binaries to unpack content: 3> Pixels = <<213,45,132,64,76,32,76,0,0,234,32,15>>. <<213,45,132,64,76,32,76,0,0,234,32,15>> 4> <<Pix1,Pix2,Pix3,Pix4>> = Pixels. ** exception error: no match of right hand side value <<213,45,132,64,76,32,76, 0,0,234,32,15>> 5> <<Pix1:24, Pix2:24, Pix3:24, Pix4:24>> = Pixels. <<213,45,132,64,76,32,76,0,0,234,32,15>> What we did on command 3 is declare what would be precisely 4 pixels of RGB colors in binary. On expression 4, we tried to unpack 4 values from the binary content. It throws an exception, because we have more than 4 segments, we in fact have 12! So what we do is tell Erlang that each variable on the left side will hold 24 bits of data. That's what Var:24 means. We can then take the first pixel and unpack it further into single color values: 6> <<R:8, G:8, B:8>> = <<Pix1:24>>. <<213,45,132>> 7> R. 213 "Yeah that's dandy. What if I only wanted the first color from the start though? will I have to unpack all these values all the time?" Hah! Doubt not! Erlang introduces more syntactic sugar and pattern matching to help you around: 8> <<R:8, Rest/binary>> = Pixels. <<213,45,132,64,76,32,76,0,0,234,32,15>> 9> R. 213 Nice, huh? That's because Erlang accepts more than one way to describe a binary segment. Those are all valid: Value Value:Size Value/TypeSpecifierList Value:Size/TypeSpecifierList where Size is always in bits and TypeSpecifierList represents one or more of the following: Type Possible values: integer | float | binary | bytes | bitstring | bits | utf8 | utf16 | utf32 This represents the kind of binary data used. Note that 'bytes' is shorthand for 'binary' and 'bits' is shorthand for 'bitstring'. When no type is specified, Erlang assumes an 'integer' type. Signedness Possible values: signed | unsigned Only matters for matching when the type is integer. The default is 'unsigned'. Endianness Possible values: big | little | native Endianness only matters when the Type is either integer, utf16, utf32, or float. This has to do with how the system reads binary data. As an example, the BMP image header format holds the size of its file as an integer stored on 4 bytes. For a file that has a size of 72 bytes, a little-endian system would represent this as <<72,0,0,0>> and a big-endian one as <<0,0,0,72>>. One will be read as '72' while the other will be read as '1207959552', so make sure you use the right endianness. There is also the option to use 'native', which will choose at run-time if the CPU uses little-endianness or big-endianness natively. By default, endianness is set to 'big'. Unit written unit:Integer This is the size of each segment, in bits. The allowed range is 1..256 and is set by default to 1 for integers, floats and bit strings and to 8 for binary. The utf8, utf16 and utf32 types require no unit to be defined. The multiplication of Size by Unit is equal to the number of bits the segment will take and must be evenly divisible by 8. The unit size is usually used to ensure byte-alignment. The TypeSpecifierList is built by separating attributes by a '-'. Some examples may help digest the definitions: 10> <<X1/unsigned>> = <<-44>>. <<"Ô">> 11> X1. 212 12> <<X2/signed>> = <<-44>>. <<"Ô">> 13> X2. -44 14> <<X2/integer-signed-little>> = <<-44>>. <<"Ô">> 15> X2. -44 16> <<N:8/unit:1>> = <<72>>. <<"H">> 17> N. 72 18> <<N/integer>> = <<72>>. <<"H">> 19> <<Y:4/little-unit:8>> = <<72,0,0,0>>. <<72,0,0,0>> 20> Y. 72 You can see there are more than one way to read, store and interpret binary data. This is a bit confusing, but still much simpler than using the usual tools given by most languages. The standard binary operations (shifting bits to left and right, binary 'and', 'or', 'xor', or 'not') also exist in Erlang. Just use the functions bsl (Bit Shift Left), bsr (Bit Shift Right), band, bor, bxor, and bnot. 2#00100 = 2#00010 bsl 1. 2#00001 = 2#00010 bsr 1. 2#10101 = 2#10001 bor 2#00101. With that kind of notation and the bit syntax in general, parsing and pattern matching binary data is a piece of cake. One could parse TCP segments with code like this: <<SourcePort:16, DestinationPort:16, AckNumber:32, DataOffset:4, _Reserved:4, Flags:8, WindowSize:16, CheckSum: 16, UrgentPointer:16, Payload/binary>> = SomeBinary. The same logic can then be applied to anything binary: video encoding, images, other protocol implementations, etc. Don't drink too much Kool-Aid: Erlang is slow compared to languages like C or C++. Unless you are a patient person, it would be a bad idea to do stuff like converting videos or images with it, even though the binary syntax makes it extremely interesting as I hinted above. Erlang is just not that great at heavy number crunching. Take note, however, that Erlang is still mighty fast for applications that do not require number crunching: reacting to events, message passing (with the help of atoms being extremely light), etc. It can deal with events in matters of milliseconds and as such is a great candidate for soft-real-time applications. A stri There's a whole other aspect to binary notation: bit strings. Binary strings are bolted on top of the language the same way they are with lists, but they're much more efficient in terms of space. This is because normal lists are linked lists (1 'node' per letter) while bit strings are more like C arrays. Bit strings use the syntax <<"this is a bit string!">>. The downside of bit strings compared to lists is a loss in simplicity when it comes to pattern matching and manipulation. Consequently, people tend to use bit strings when storing text that won't be manipulated too much or when space efficiency is a real issue. We'll see strings and bit strings more in depth in chapter 9. For the time being, that's all we need to know. Note: Even though bit strings are pretty light, you should avoid using them to tag values. It could be tempting to use string literals to say {<<"temperature">>,50}, but always use atoms when doing that. Previously in this chapter, atoms were said to be taking only 4 or 8 bytes in space, no matter how long they are. By using them, you'll have basically no overhead when copying data from function to function or sending it to another Erlang node on another server. Conversely, do not use atoms to replace strings because they are lighter. Strings can be manipulated (splitting, regular expressions, etc) while atoms can only be compared and nothing else. Binary Comprehensions Binary comprehensions are to bit syntax what list comprehensions are to lists: a way to make code short and concise. They are relatively new in the Erlang world as they were there in previous revisions of Erlang, but required a module implementing them to use a special compile flag in order to work. Since the R13B revisions (those used here), they've become standard and can be used anywhere, including the shell: 1> [ X || <<X>> <= <<1,2,3,4,5>>, X rem 2 == 0]. [2,4] The only change in syntax from regular list comprehensions is the <- which became <= and using binaries (<<>>) instead of lists ([]). Earlier in this chapter we've seen an example where there was a binary value of many pixels on which we used pattern matching to grab the RGB values of each pixel. It was alright, but on larger structures, it would become possibly harder to read and maintain. The same exercise can be done with a one-line binary comprehension, which is much cleaner: 2> Pixels = <<213,45,132,64,76,32,76,0,0,234,32,15>>. <<213,45,132,64,76,32,76,0,0,234,32,15>> 3> RGB = [ {R,G,B} || <<R:8,G:8,B:8>> <= Pixels ]. [{213,45,132},{64,76,32},{76,0,0},{234,32,15}] Changing <- to <= let us use a binary stream as a generator. The complete binary comprehension basically changed binary data to integers inside tuples. Another binary comprehension syntax exists to let you do the exact opposite: 4> << <<R:8, G:8, B:8>> || {R,G,B} <- RGB >>. <<213,45,132,64,76,32,76,0,0,234,32,15>> Be careful, as the elements of the resulting binary require a clearly defined size if the generator returned binaries: 5> << <<Bin>> || Bin <- [<<3,7,5,4,7>>] >>. ** exception error: bad argument 6> << <<Bin/binary>> || Bin <- [<<3,7,5,4,7>>] >>. <<3,7,5,4,7>> http://www.eonblast.com/blog/optimizing-erlualib-calls/ http://www.infoq.com/presentations/Building-Highly-Available-Systems-in-Erlang https://www.erlang.org/doc/getting_started/conc_prog.html#message-passing https://news.ycombinator.com/item?id=38903276 The Erlang Ecosystem [video] (youtube.com) https://blog.stenmans.org/theBeamBook/ The Erlang Runtime System https://news.ycombinator.com/item?id=39342392 The Erlang Runtime System (stenmans.org) https://jlouisramblings.blogspot.com/2010/12/response-to-erlang-overhyped-or.html https://pliutau.com/my-first-experience-with-gleam-lang/ https://news.ycombinator.com/item?id=41366614 My first experience with Gleam Language (pliutau.com) https://ferd.ca/my-blog-engine-is-the-erlang-build-tool.html https://news.ycombinator.com/item?id=41425467 My Blog Engine Is the Erlang Build Tool (ferd.ca) # Local Variables: # coding: utf-8-unix # End: