Monday, 11 November 2013
Incidentally
I don't subscribe to the opinion, incidentally, that the purpose of a Hello World program is to illustrate the character of a programming language. No, the point of Hello World is to remove as far as possible any foibles or characteristics peculiar to the language so that you can focus on understanding your development process irrespective of what you are developing. Can I create a source, compile it, link it, load it, run it and find where the output went? It's a kind of tracer bullet for your tool set, a barium meal for your development environment.
Friday, 8 November 2013
Hello Again World
So let us start at the Place Where People Start. Write a script in Erlang that will write the text "Hello World" to the terminal.
I call it a "script" because that seems appropriate when the program is short and is being executed directly from the source code.
Our Hello World script looks like this:
main([]) ->
io:format("Hello World").
This goes in a file called hello.erl.
Now, the first line of the script is left blank. This is because escript considers this line to be reserved for system-specific commands to the shell to nominate the program that runs the script. I'll leave this blank as I'm on Windows 7 here and that trick does not work.
The rest of the file consists of a definition of the function main(). I told you, it's like C. The square brackets [] are a list in which the system will pass the arguments on the command line. At first we'll take this to be empty and not use any arguments. The symbol -> introduces the body of the definition. This is a call to the format function in the io library, indicated with a colon thus io:format. To this function we pass a string containing placeholders and a list of identifiers whose values will be slotted into the string - so it does what printf would do in C. In this case we are just printing a single string, so no placeholders and no list of arguments. The full top . terminates the function definition.
The function io:format is documented in the Stdlib document. It returns just the atom ok so of course here we are using a function for its side-effects, that is to say writing text to the terminal. You can call io:format three ways: the first way just pass a string, to be output: the second way also pass a list
of values to substitute into the string; and the third way you first specify the output channel, which would otherwise default to standard output. So these are all the same:
io:format("Hello World").
io:format("Hello World", []).
io:format(standard_io, "Hello World", []).
In the Erlang jargon these would be described as format/1, format/2 and format/3.
When you execute the script from the command line with the escript command, escript executes the program starting from the main function.
So in Erlang jargon we would call this the function main/0, meaning it's called main and it takes no arguments.
You run this with the command escript like this:
C:\Users\polly\Erlang>escript hello.erl
Hello World
So what about passing arguments? What happens if I add something to the command line?
C:\Users\polly\Erlang>escript hello.erl charlie
escript: exception error: {function_clause,[{local,main,[["charlie"]]}]}
in function escript:code_handler/4 (escript.erl, line 838)
in call from erl_eval:local_func/5 (erl_eval.erl, line 467)
in call from escript:interpret/4 (escript.erl, line 774)
in call from escript:start/1 (escript.erl, line 277)
in call from init:start_it/1 (init.erl, line 1054)
in call from init:start_em/1 (init.erl, line 1034)
The escript returns a run time error message because although we have defined a function main/0 to process no arguments we have tried to call main/1, the same function with one argument, which has not been defined.
Erlang tries a pattern match of the arguments it has in its hands and the possible sets of arguments to the main function and finding none that match it declares an error and halts processing.
So we can now allow for an argument to the function by adding a clause to our existing definition: remove the full stop at the end and change this to a semicolon and we can add a clause for the case with an argument, which we call Arg:
main([]) ->
io:format("Hello World");
main([Arg]) ->
io:format("Hello ~s", [Arg]).
So now [Arg] is a list containing a single identifier Arg which will be set to the argument on the command line after the name of the script.
Then within the format() function we add a second argument, the list containing [Arg], and add a placeholder ~s in the string to indicate where we want this to be slotted in to the string.
So now this will work with or without an extra argument:
C:\Users\polly\Erlang>escript hello.erl
Hello World
C:\Users\polly\Erlang>escript hello.erl charlie
Hello charlie
C:\Users\polly\Erlang>
Which of course now poses the question:
C:\Users\polly\Erlang>escript hello.erl curly larry mo
escript: exception error: {function_clause,[{local,main,[["curly","larry","mo"]]
}]}
in function escript:code_handler/4 (escript.erl, line 838)
in call from erl_eval:local_func/5 (erl_eval.erl, line 467)
in call from escript:interpret/4 (escript.erl, line 774)
in call from escript:start/1 (escript.erl, line 277)
in call from init:start_it/1 (init.erl, line 1054)
in call from init:start_em/1 (init.erl, line 1034)
We've allowed for one argument but more than one is still an error. No pattern match for it, you see.
So, we want the final option to take care of two or more items on the command line. The expression [X|XS] is the Erlang code for a list whose first element is X and the rest of which is XS. So we want to match against this as follows:
main([]) ->
io:format("Hello World");
main([Arg]) ->
io:format("Hello ~s", [Arg]);
main([Arg|More]) ->
io:format("Hello ~s and~n", [Arg]),
main(More).
Right, so here we have matched against [Arg|More] in our argument list for the main function. Note that this doesn't just match the pattern - it doesn't just say, yes, your argument list matches the pattern [Arg|More] - it also assigns the parts of the argument list, the head and tail, to the identifiers you supply, all in one statement. This I have to admit is neat, remembering that in Lisp I would first check that I had a list with a head and a tail and then if this were so go back and get the CAR and the CDR a couple of lines later. Not so neat.
How to handle this case? We write the first element Arg to the output and then re-call the main() function to process More, the rest of the argument list. The first line here now ends with a comma, meaning there are further lines within this block, to be executed in sequence - - so the comma does what a PROGN would do in Lisp.
C:\Users\polly>escript hello.erl
Hello World
C:\Users\polly>escript hello.erl charlie
Hello charlie
C:\Users\polly>escript hello.erl curly larry moe
Hello curly and
Hello larry and
Hello moe
C:\Users\polly>
I call it a "script" because that seems appropriate when the program is short and is being executed directly from the source code.
Our Hello World script looks like this:
main([]) ->
io:format("Hello World").
This goes in a file called hello.erl.
Now, the first line of the script is left blank. This is because escript considers this line to be reserved for system-specific commands to the shell to nominate the program that runs the script. I'll leave this blank as I'm on Windows 7 here and that trick does not work.
The rest of the file consists of a definition of the function main(). I told you, it's like C. The square brackets [] are a list in which the system will pass the arguments on the command line. At first we'll take this to be empty and not use any arguments. The symbol -> introduces the body of the definition. This is a call to the format function in the io library, indicated with a colon thus io:format. To this function we pass a string containing placeholders and a list of identifiers whose values will be slotted into the string - so it does what printf would do in C. In this case we are just printing a single string, so no placeholders and no list of arguments. The full top . terminates the function definition.
The function io:format is documented in the Stdlib document. It returns just the atom ok so of course here we are using a function for its side-effects, that is to say writing text to the terminal. You can call io:format three ways: the first way just pass a string, to be output: the second way also pass a list
of values to substitute into the string; and the third way you first specify the output channel, which would otherwise default to standard output. So these are all the same:
io:format("Hello World").
io:format("Hello World", []).
io:format(standard_io, "Hello World", []).
In the Erlang jargon these would be described as format/1, format/2 and format/3.
When you execute the script from the command line with the escript command, escript executes the program starting from the main function.
So in Erlang jargon we would call this the function main/0, meaning it's called main and it takes no arguments.
You run this with the command escript like this:
C:\Users\polly\Erlang>escript hello.erl
Hello World
So what about passing arguments? What happens if I add something to the command line?
C:\Users\polly\Erlang>escript hello.erl charlie
escript: exception error: {function_clause,[{local,main,[["charlie"]]}]}
in function escript:code_handler/4 (escript.erl, line 838)
in call from erl_eval:local_func/5 (erl_eval.erl, line 467)
in call from escript:interpret/4 (escript.erl, line 774)
in call from escript:start/1 (escript.erl, line 277)
in call from init:start_it/1 (init.erl, line 1054)
in call from init:start_em/1 (init.erl, line 1034)
The escript returns a run time error message because although we have defined a function main/0 to process no arguments we have tried to call main/1, the same function with one argument, which has not been defined.
Erlang tries a pattern match of the arguments it has in its hands and the possible sets of arguments to the main function and finding none that match it declares an error and halts processing.
So we can now allow for an argument to the function by adding a clause to our existing definition: remove the full stop at the end and change this to a semicolon and we can add a clause for the case with an argument, which we call Arg:
main([]) ->
io:format("Hello World");
main([Arg]) ->
io:format("Hello ~s", [Arg]).
So now [Arg] is a list containing a single identifier Arg which will be set to the argument on the command line after the name of the script.
Then within the format() function we add a second argument, the list containing [Arg], and add a placeholder ~s in the string to indicate where we want this to be slotted in to the string.
So now this will work with or without an extra argument:
C:\Users\polly\Erlang>escript hello.erl
Hello World
C:\Users\polly\Erlang>escript hello.erl charlie
Hello charlie
C:\Users\polly\Erlang>
Which of course now poses the question:
C:\Users\polly\Erlang>escript hello.erl curly larry mo
escript: exception error: {function_clause,[{local,main,[["curly","larry","mo"]]
}]}
in function escript:code_handler/4 (escript.erl, line 838)
in call from erl_eval:local_func/5 (erl_eval.erl, line 467)
in call from escript:interpret/4 (escript.erl, line 774)
in call from escript:start/1 (escript.erl, line 277)
in call from init:start_it/1 (init.erl, line 1054)
in call from init:start_em/1 (init.erl, line 1034)
We've allowed for one argument but more than one is still an error. No pattern match for it, you see.
So, we want the final option to take care of two or more items on the command line. The expression [X|XS] is the Erlang code for a list whose first element is X and the rest of which is XS. So we want to match against this as follows:
main([]) ->
io:format("Hello World");
main([Arg]) ->
io:format("Hello ~s", [Arg]);
main([Arg|More]) ->
io:format("Hello ~s and~n", [Arg]),
main(More).
Right, so here we have matched against [Arg|More] in our argument list for the main function. Note that this doesn't just match the pattern - it doesn't just say, yes, your argument list matches the pattern [Arg|More] - it also assigns the parts of the argument list, the head and tail, to the identifiers you supply, all in one statement. This I have to admit is neat, remembering that in Lisp I would first check that I had a list with a head and a tail and then if this were so go back and get the CAR and the CDR a couple of lines later. Not so neat.
How to handle this case? We write the first element Arg to the output and then re-call the main() function to process More, the rest of the argument list. The first line here now ends with a comma, meaning there are further lines within this block, to be executed in sequence - - so the comma does what a PROGN would do in Lisp.
C:\Users\polly>escript hello.erl
Hello World
C:\Users\polly>escript hello.erl charlie
Hello charlie
C:\Users\polly>escript hello.erl curly larry moe
Hello curly and
Hello larry and
Hello moe
C:\Users\polly>
Thursday, 7 November 2013
See How It Runs!
The Erlang compiler erlc compiles Erlang code into semicompiled code that runs on a virtual machine. Each module of code occupies one file of source code with the extension .erl and compiles to byte code in a file with extension .beam. The beam files are executed by a virtual machine under there somewhere.
You can load these into the Erlang Shell for evaluation.
However there is also a modest tool called escript that will execute a file of raw Erlang code if you write it up appropriately - with this you can try out a few short Erlang scripts, maybe open your Kernighan & Ritchie and imagine it's like C.
The REPL interface is fine for its purpose but I also like the DBM/JDI user interface (Don't Bother Me, Just Do It).
Escript will also execute compiled code if you set it out correctly. So someone could issue an Erlang application as a compiled escript file (which is where this is heading...).
You can load these into the Erlang Shell for evaluation.
However there is also a modest tool called escript that will execute a file of raw Erlang code if you write it up appropriately - with this you can try out a few short Erlang scripts, maybe open your Kernighan & Ritchie and imagine it's like C.
The REPL interface is fine for its purpose but I also like the DBM/JDI user interface (Don't Bother Me, Just Do It).
Escript will also execute compiled code if you set it out correctly. So someone could issue an Erlang application as a compiled escript file (which is where this is heading...).
Wednesday, 6 November 2013
See! It lives!
Everything you need to start development with the Erlang language is available as a download from www.erlang.org at http://www.erlang.org/download.html. The download is 90MB ish. The download installs all the tools - compiler, REPL shell etc - and documentation and the "OTP" which provides comprehensive libraries including even code to build a database and create GUI applications. What they call a platform. Anyway they seem to have thought of everything.
BTW, OTP stands for Outlaw Techo Psychobitch - there is an excellent video presentation also available online (Erlang The Movie The Sequel) that clarifies the marketing decisions behind unexpectedly vivid nomenclature.
When it's installed you get a link to a REPL Shell and a link to the Documentation. The documentation link takes you to an HTML home page within the documentation with links to reference files and to a Quickstart Guide. The Documentation you get with the download is comprehensive. There are matching HTML and PDF versions of the documentation for each module. These are housed in the sub-folders for each module so I swept up all the PDF files in the whole Erlang installation and copied them into one central documents folder to make them easier to browse through. Personally I like documents in PDF best.
The Quick Start tells you how to write some trial functions in a file and load them into your Erlang Shell. A Quick Start is important to give the right first impressions. It needs to (a) be quick but more importantly it needs to (b) start. This one annoyed me at first because I ran the Windows Erlang shell from the default setup prompt. You can't load the example programs into that because you are not in the right folder. Hah! You can use the cd() shell function to change your working folder but I didn't discover this until later. The Help option on the shell just shows you the version number. However there is a command help() in the shell that lists the commands, but I didn't find that until later either.
You can't define functions in the shell - - which some have complained about. I don't think you can define functions in the Haskell shell either. Best place for a function definition is in a separate little file anyway, surely?
Anyway the command to exit the shell is q(). The full stop at the end matters. Better to run the command line Erlang shell with the command erl from the command line and then you can be in whatever folder you want:
C:\Users\polly\Erlang>erl
Eshell V5.10.2 (abort with ^G)
1> 2+2.
4
2> q().
ok
3>
C:\Users\polly\Erlang>
See, Igor! It lives! It lives!
BTW, OTP stands for Outlaw Techo Psychobitch - there is an excellent video presentation also available online (Erlang The Movie The Sequel) that clarifies the marketing decisions behind unexpectedly vivid nomenclature.
When it's installed you get a link to a REPL Shell and a link to the Documentation. The documentation link takes you to an HTML home page within the documentation with links to reference files and to a Quickstart Guide. The Documentation you get with the download is comprehensive. There are matching HTML and PDF versions of the documentation for each module. These are housed in the sub-folders for each module so I swept up all the PDF files in the whole Erlang installation and copied them into one central documents folder to make them easier to browse through. Personally I like documents in PDF best.
The Quick Start tells you how to write some trial functions in a file and load them into your Erlang Shell. A Quick Start is important to give the right first impressions. It needs to (a) be quick but more importantly it needs to (b) start. This one annoyed me at first because I ran the Windows Erlang shell from the default setup prompt. You can't load the example programs into that because you are not in the right folder. Hah! You can use the cd() shell function to change your working folder but I didn't discover this until later. The Help option on the shell just shows you the version number. However there is a command help() in the shell that lists the commands, but I didn't find that until later either.
You can't define functions in the shell - - which some have complained about. I don't think you can define functions in the Haskell shell either. Best place for a function definition is in a separate little file anyway, surely?
Anyway the command to exit the shell is q(). The full stop at the end matters. Better to run the command line Erlang shell with the command erl from the command line and then you can be in whatever folder you want:
C:\Users\polly\Erlang>erl
Eshell V5.10.2 (abort with ^G)
1> 2+2.
4
2> q().
ok
3>
C:\Users\polly\Erlang>
See, Igor! It lives! It lives!
Friday, 14 December 2012
December is Here
No, that last code still looks a mess. But it is written against the requirement for a single block of code that can be pasted into an on-line exercise. December is here and as far as Clojure is concerned I'm still tinkering with basic exercises. How do they manage, those folks who undertake to learn a new language every year? They use the new language to solve their real problems. Hmm.
Wednesday, 14 November 2012
Just the Squares Continued
Yes, putting that function together in a different way:-
(def just-squares
(fn [s]
(let [split-string (fn [s] (clojure.string/split s #",")),
join-strings (fn [ss] (clojure.string/join "," ss)),
square? (fn [n] (let [root (int (. Math sqrt n))] (= (* root root) n))),
filter-squares (fn [ns] (filter square? ns)),
strs->ints (fn [ss] (map #(. Integer parseInt %) ss)),
ints->strs (fn [ns] (map str ns))]
(-> s split-string strs->ints filter-squares ints->strs join-strings))))
(def just-squares
(fn [s]
(let [split-string (fn [s] (clojure.string/split s #",")),
join-strings (fn [ss] (clojure.string/join "," ss)),
square? (fn [n] (let [root (int (. Math sqrt n))] (= (* root root) n))),
filter-squares (fn [ns] (filter square? ns)),
strs->ints (fn [ss] (map #(. Integer parseInt %) ss)),
ints->strs (fn [ns] (map str ns))]
(-> s split-string strs->ints filter-squares ints->strs join-strings))))
Tuesday, 13 November 2012
Just the Squares
Today's exercise is to take a string that contains numbers separated by commas, like this:
"3,4,5,6,7,8,9"
and return the same except containing only the numbers that are perfect squares, which in this case would be
"4,9"
So the first step is to break up that string into the individual numbers. In the Clojure String library we get the split function, which takes your string and a regular expression that determines what part of the string is to be used to split. We're using just about the simplest possible option, just chopping through the commas. A function to do this would look so:
(defn split-string [s]
(clojure.string/split s #","))
This gives us a sequence of the separated strings:-
user=> (split-string "4,5,45,6,7,67")
["4" "5" "45" "6" "7" "67"]
When we want to join these back again to restore the single string we have split's partner join, so:-
(defn join-string [ss]
(clojure.string/join "," ss))
We just specify the string "," to be added between the strings in our sequence.
user=> (join-string ["4" "5" "45" "6" "7" "67"])
"4,5,45,6,7,67"
Now we will want to get the integer values of these strings. So we dip into the Java class Integer and bring back the method parseInt. I love it when you can step between languages and they play nicely together. In the system I use professionally I can step from C to assembler and back again. It's similar in that Clojure has Java hiding inside and C has assembler hiding inside. Anyway to change a string to an integer I can summon the Integer class and call the parseInt method on it, like this:-
user=> (. Integer parseInt "123")
123
That's a macro but it can become a function quite easily:-
user=> (#(. Integer parseInt %) "123")
123
And we will want to convert these integers back into strings: the Clojure function str will do this:-
user=> (str 3)
"3"
We will want a filter function that will decide whether a number is a square. For this let's dip into Java again and get the square root method from the Math class:-
user=> (. Math sqrt 2.0)
1.4142135623730951
So if I take the integer part of the square root (the Clojure function int will give this) and square this and compare with the original number that indicates whether it is was a square number. Along these lines:-
(defn is-square [n]
(let [root (int (. Math sqrt n))]
(= (* root root) n)))
user=> (is-square 100)
true
user=> (is-square 101)
false
user=> (is-square 99)
false
OK so putting the parts together. The first version of my function just-squares will open up the string into the individual numbers and then put them together again:-
(def just-squares
(fn [s]
(join-string (split-string s))))
user=> (def s "4,5,6,7,8,9")
#'user/s
user=> (just-squares s)
"4,5,6,7,8,9"
So far so good. Now convert them to integers and back again.
(def just-squares
(fn [s]
(join-string
(map str
(map #(. Integer parseInt %) (split-string s))))))
user=> (just-squares s)
"4,5,6,7,8,9"
Still works. Now add that filter to allow only the square ones:-
(def just-squares
(fn [s]
(join-string
(map str
(filter (fn [n] (let [root (int (. Math sqrt n))](= (* root root) n)))
(map #(. Integer parseInt %) (split-string s)))))))
user=> (just-squares s)
"4,9"
Ok so this works. Now to make this code complete in itself I should fill in the definitions of those functions split-string and join-string, so we get this:-
(def just-squares
(fn [s]
(#(clojure.string/join "," %)
(map str
(filter (fn [n] (let [root (int (. Math sqrt n))](= (* root root) n)))
(map #(. Integer parseInt %) (clojure.string/split s #",")))))))
Hmm. Well, that works on the 4Clojure page but I don't like it. Maybe there's scope to use the -> macro to link the sections together instead of nesting them. Or use more lets to give the sections some names.
Also a more idiomatic name for is-square would have been square?.
"3,4,5,6,7,8,9"
and return the same except containing only the numbers that are perfect squares, which in this case would be
"4,9"
So the first step is to break up that string into the individual numbers. In the Clojure String library we get the split function, which takes your string and a regular expression that determines what part of the string is to be used to split. We're using just about the simplest possible option, just chopping through the commas. A function to do this would look so:
(defn split-string [s]
(clojure.string/split s #","))
This gives us a sequence of the separated strings:-
user=> (split-string "4,5,45,6,7,67")
["4" "5" "45" "6" "7" "67"]
When we want to join these back again to restore the single string we have split's partner join, so:-
(defn join-string [ss]
(clojure.string/join "," ss))
We just specify the string "," to be added between the strings in our sequence.
user=> (join-string ["4" "5" "45" "6" "7" "67"])
"4,5,45,6,7,67"
Now we will want to get the integer values of these strings. So we dip into the Java class Integer and bring back the method parseInt. I love it when you can step between languages and they play nicely together. In the system I use professionally I can step from C to assembler and back again. It's similar in that Clojure has Java hiding inside and C has assembler hiding inside. Anyway to change a string to an integer I can summon the Integer class and call the parseInt method on it, like this:-
user=> (. Integer parseInt "123")
123
That's a macro but it can become a function quite easily:-
user=> (#(. Integer parseInt %) "123")
123
And we will want to convert these integers back into strings: the Clojure function str will do this:-
user=> (str 3)
"3"
We will want a filter function that will decide whether a number is a square. For this let's dip into Java again and get the square root method from the Math class:-
user=> (. Math sqrt 2.0)
1.4142135623730951
So if I take the integer part of the square root (the Clojure function int will give this) and square this and compare with the original number that indicates whether it is was a square number. Along these lines:-
(defn is-square [n]
(let [root (int (. Math sqrt n))]
(= (* root root) n)))
user=> (is-square 100)
true
user=> (is-square 101)
false
user=> (is-square 99)
false
OK so putting the parts together. The first version of my function just-squares will open up the string into the individual numbers and then put them together again:-
(def just-squares
(fn [s]
(join-string (split-string s))))
user=> (def s "4,5,6,7,8,9")
#'user/s
user=> (just-squares s)
"4,5,6,7,8,9"
So far so good. Now convert them to integers and back again.
(def just-squares
(fn [s]
(join-string
(map str
(map #(. Integer parseInt %) (split-string s))))))
user=> (just-squares s)
"4,5,6,7,8,9"
Still works. Now add that filter to allow only the square ones:-
(def just-squares
(fn [s]
(join-string
(map str
(filter (fn [n] (let [root (int (. Math sqrt n))](= (* root root) n)))
(map #(. Integer parseInt %) (split-string s)))))))
user=> (just-squares s)
"4,9"
Ok so this works. Now to make this code complete in itself I should fill in the definitions of those functions split-string and join-string, so we get this:-
(def just-squares
(fn [s]
(#(clojure.string/join "," %)
(map str
(filter (fn [n] (let [root (int (. Math sqrt n))](= (* root root) n)))
(map #(. Integer parseInt %) (clojure.string/split s #",")))))))
Hmm. Well, that works on the 4Clojure page but I don't like it. Maybe there's scope to use the -> macro to link the sections together instead of nesting them. Or use more lets to give the sections some names.
Also a more idiomatic name for is-square would have been square?.
Subscribe to:
Posts (Atom)