>> LENTCZNER: Thanks everyone for showing
up for a crazy taste of Haskell.
I'm Mark [INDISTINCT].
So, Haskell is scary, you've all heard it.
You've all heard the runs on Reddit, right?
You know, it's got no state, you know, who
wants Lazynest?
Lazynest is, it's lazy, you know, and PHP
has, you know, [INDISTINCT], screw that.
And after all, didn't we just spend the last
20 years like making dynamic languages rule
the world?
I mean, we did, didn't we?
Right?
And my ads--okay.
But Haskell is actually really scary cool.
It is functional, which turns out to be kind
of scary and cool and it's lazy and it has
high order of functions and type in friends
and it has monads.
Okay.
This is why I got hooked in the--in to Haskell.
I've actually been programming for more than
half the time profession has even existed
and so three years ago when I ran in to Haskell
or sort of looked at Haskell again, I was
shocked and really enjoyed that like, My god.
There was like something new to learn about
programming.
I mean, something really new.
So that was fun.
It really does twist your mind, I mean, you
know, Haskell is really--makes you really
rethink a lot of stuff but the two things
that I think it really got me is, look the
language is beautiful, you write code and
you just look at it, and you're, like, Wow,
those are like the nicest seven lines of code
I've written in the last month.
Like they're just gorgeous, you know, and
so it's really a beautiful code.
So, because of that reason, a lot of code
to show you.
I have 50 more slides and every one of them
I think has code on it.
So, we're going to go really fast with you
all Googlers so it will be no problem.
Mute we need a mute on some DC, hopefully
some slide.
No?
Mute.
I don't know who it is.
All right.
The code is going to look like crazy-moon
language, it's just insane.
You know, it's just totally different but
not really but it is different, so just be
brave, okay?
Just be brave.
All right.
There is git repo, if you want to pull everything
a little, of course, I edited the slides 15
minutes before we started.
So it's not quite up to date, you can just
Google for Haskell-Amuse-Bouche and the fist
hit is this repo and I haven't pushed yet
to get the latest stuff so it's a little bit
out of date from the slides.
But it has not only the slides, it has all
the source code I'm going to show you and
Haskell files that you can load up in the
repo and edit.
So, you can play along as well, If you want.
But I'm not going to give you too much time.
So, let's start with something really, really
familiar.
This is Bouche, not Haskell, right?
And this stuff should look pretty familiar
to anyone, there we go, a little bigger.
All right.
This is just standard pipelines on the shelf.
Okay, what do they all do?
They all take input, they process it, they
produce the output, as soon as they are able,
that's actually a feature of most Unix commands.
Most of those commands do not modify any state.
Actually, I chose ones which didn't modify
any state at all, right?
But those are just small things you do, right?
In short, those are functional, pure and lazy,
right?
So, you're actually used to this stuff.
It's actually not as crazy and weird as you
think and, you know, the vast shell or the
shell and the concept of the shell has been
around for how many decades now and we still
all use it, so it might have some value there.
Okay.
Let's rewrite that in Haskell.
Hopefully that's big enough for you to read.
So there are some gullible [INDISTINCT] on
the first line which we're going to ignore
for this millisecond.
Because the next line, we're going to talk
about a lot and if you stick that in the file
called run hask in a Part1.hs and run it;
you give it this poem it's input and surprise,
it sorts the lines and gives you that poem
as output.
Then master [INDISTINCT] or those who the
reference.
Okay.
So, let's look at the actual processing, all
right, which is right on that first line.
It is not exactly rocket science.
We're going to define a function called process
which takes some t some text and it's going
to call some functions.
And the only difference from what you are
used to is that functional arguments don't
go in parenthesis, we just used parenthesis
for grouping.
So be brave, just look at the code.
Get the lines of t, it means take the string
and turn it into an array of strings or a
list of strings, technically a list.
Sort that list and then unlines which is another
library function which takes a list of strings,
slams new lines into them and throws it all
together.
All right.
So, the first line should be like dead clear,
right?
I don't see anyone looking strange you can
like, okay, everything is nodding yes, so
good.
Okay. now, I know I had to take you back to
high school and everyone hates that but in
high school you hate algebra because, you
know, you learn this, right?
F of G of X can be written as this funky thing
with a dot in the middle.
All right.
This is function composition F of G of X,
all right, so you can see that.
While in Haskell, it turns out, that's actually
a programming language feature.
So in the same way, we can do that up here,
we can fact it--we can sort of rewrite this,
this way.
All right.
So, the function process of some text t is
applied to that text t, the function lines
composed with sort composed with unlines,
all right.
So that data operator is exactly like it was
in algebra.
All it just means is apply the thing on the
left to the result of having applied the thing
on the right, so unlines it then sort it,
I mean, lines it then sort it then unlines
it.
All right.
It reads kind of right to left which might
seem kind of strange to you because bash things
go in the other direction.
But look up here, right?
I mean, it's just getting rid of all these
parenthesis.
Okay.
Now, here comes the real [INDISTINCT].
We can actually do algebraic simplification
or sort of the equivalent.
So, work with me here.
If process of--it takes some input, right?
Process a function applied to some input t
and all it is, is this composite function,
these three things applied in sequence to
t--in Haskell you should get rid of the t's
because we can say, Well, process--by the
way, the tip marks are just valid characters
for identifiers and I had to name them all
three different things so they can go on the
same file.
The process double tip mark is a function
that is just the composition of these three
functions.
Have I lost anyone yet?
Good.
Onward.
Okay.
So we can code a bunch of other ones.
We can say sorting lines is unlines.sort lines,
right?
Compose those three.
We can reverse lines, we can take the first
two lines because take two is a--no, it does
what you think, right?
So, anyone see a pattern?
It's a pattern, it's algebra, we can factor
it.
So, we can actually write a function called
by lines which takes a function F and it's
equal to--we just write the same expression
but stick our variable in the middle, right?
So by lines of F is the same as having written
unlines.f.lines.
So, now I can rewrite those three functions.
Sort lines write by line sort.
Reverse lines, by lines reverse.
First two lines, by lines take two.
Yes, yes, no, there you go, [INDISTINCT] that's
good.
All right.
Let's write our own.
Okay.
I'm going to write a new function, sort of
from scratch here it's called indent.
This funny line at the top is a tight declaration,
it says indent is a function that starts with
a string, takes a string argument and turns
it in to a string as a string result.
It's kind of very functional style in notation,
oddly enough.
All right.
Anyone noticed that this is the first type
I have shown you anywhere?
All the other code is completely 100% valid
Haskell you'll find it in the--in the files
that are with the repo exactly as I wrote
it here with no additional typing.
So, Haskell is a language that is really strongly
typed and yet we didn't have to type any types
because Haskell is type [INDISTINCT] which
means that when you type some--when you write
on a code, the expression, Haskell figures
out what type that must be when you write
a type to a first order of approximation,
98% of time.
The type is for you, not for the compiler.
In fact, the compiler ignores what you write
as a type, figures out what the type is on
its own and then says, Did I get it right?
Now, you might think that's kind of crazy
like aren't--like, it shouldn't be, but no.
The compiler it's there because we write the
types as communication between programmers
and the communication of our intent and the
compiler is actually checking us.
Checking to make sure we're honest.
Okay.
So indent is a function from string to string,
it's really complicated, it takes an argument
S, ended up pens at the double plus operator,
a bunch of spaces to the front.
Great.
So, we should be able to follow our pattern.
All right.
By line sort by lines reverse and say by lines
and dent, anyone want to guess?
Boom, it doesn't work.
And the worst problem is if you put this in
the [INDISTINCT] you'll see this horrible
thing down there.
That is the Haskell compilers idea of poetry,
it's writing you a kind of a love poem but
it's from a compiler and so, as a consequence,
we ignore it.
Do you agree with that?
This is just crazy moon poetry and you just
have to deal with the fact that it's there
and most of the time you just figure what
line number it is and go looking at them.
Okay.
What is the problem?
The problem is, all our other functions sort
and reverse took a list of lines and did something
to them but we wrote something which takes
a string.
Okay.
So, we're going to use something called map.
Map to the rescue now, you probably all at
this day and age program did a language that
has a collections library and that collections
library has either a four each or a map function,
right?
And usually map takes a functional argument,
a function pointer or it depends on the language
your working in and iterates through the collection
applying the function and gathering up all
the results and returning you a new collection.
This is nothing new.
Map exist and you--I'm certain everyone here
has been exposed to a map function and just
to make sure we are to realize what we're
talking about.
I gave you some examples, compare if we map
reverse over this list of strings, right,
it reverses each individual string versus
mapping reverse to the list of strings which
map reverses the list itself, got it?
All right.
The same thing is like sorting.
some of you might at this point be scratching
your nose and go, Wait, does reverse work
on a list of strings or does reverse work
on a string?
The answer to that question is, yes it does
both.
Okay.
So, to indent each line of our poem, right?
I have to do something by lines but I better
map, all right, by lines expects a function
over a list.
So, I'm going to map indent over that list.
By lines map indent.
I could also factor that whole pattern out
because we had a thing called by lines, have
it each line, given a function--now, I've
written this--now I've written a type.
Given a function from a string to a string,
notice the parenthesis and the string return
a new string.
So, let's--let's look at what's going to happen,
each line F is going to take this--it's going
to take a string, apply lines to it, give
us a list of lines, map our function F over
each individual line and then unlines the
result back together into one big string to
return.
So, each line is now useful utility function
which does something--apply something to each
individual line.
Yes?
>> When you write maps space indent is that
the same as indent.map?
>> LENTCZNER: No, it is not.
So, the question was when you write maps space
indent is that the same as indent.map?
No, it is not.
We are passing indent as a function, as an
argument to map.
>> Okay.
Map is a function.
>> LENTCZNER: Map is a function, right.
There's no period or object.
There's no receiver in this language.
It is not object oriented.
So, map is just a pure function at the top
there but we're going to--you're getting close
to a problem.
Anyway, close to [INDISTINCT] so indent line,
we can do it this way or we can use this combinator
of this thing we just build called each line--each
line indent and then if you run them either
you get indeed an indented poem, it's very
exciting.
But wait, where is the second argument to
map?
I told you back here, map takes a function
and a list and returns a list.
In fact, at this point, looking at that top
line should hopefully be kind of clear that
that's what that says.
It takes, right?
It starts with it takes a function from A
to B, a list of As and gives you a list of
Bs but in this code up here, there is no second
argument to map.
Where is it?
All right.
Because it's not there.
Because map of F--so look at map this way,
we can say map takes a function from A to
B and because [INDISTINCT] just trust me.
I can parenthesize the remainder.
So you can think of map as taking a function
from A to B and turning it into a function
from list of A to list of B, got that?
If I just applied map to the first argument,
I get back a new function it's called [INDISTINCT]
it's [INDISTINCT] and I get back a new function,
a function list of A to B, it turns, our indent
which only works on a string to a version
of indent which works on a list of strings.
We call this lifting or lifting up, map is
kind of bringing our simple function up into
the world of lists and that's what's going
on.
All right.
And we'll write some more code here, quickly.
I want to yell because we like yelling, right?
So, this is another function from string to
string and so, I'm going to yell and I'm going
to map this function called to operate from
the library listed uppercases characters.
And I map that over the string and I append
a bunch of exclamations, and I'm going to
write yell each line and I'm going to use
our each line function to apply this to each
line of the poem.
And indeed what happens?
I've yelled each line of the poem, not particularly
exciting, it's pretty much what we did before.
But what if I want to yell each word?
Well, we're very, very lucky, there's actually
like lines and unlines, the library has words
and unwords.
So, we can define equivalently a function
called each word that given a function will
apply it to each word of the string.
So, right?
We call words which breaks it up into a list
of words.
We map F in this case, yell over that list
of words and then we unwords to put it all
back together.
And when we go off and do this, crap it all
comes out on one line, right?
Because it broke all the words apart all the
words got slammed together with spaces and
we lost all the lines.
So, what do we really want to do?
We want to yell by words by lines.
Well, guess what?
We can actually write yet another helper function.
Each word on each line, all right?
Given a function F, this will apply to each
word on each line by wrapping our function
in each word and that we're calling each word
on our function and then calling each line
on that.
And indeed, if I apply that, I get this, right?
And I had managed to preserve the lines and
do it every single word.
Notice that I have used the very functions
we have defined ourselves as functions to
more functions we've defined ourselves, right?
So, what bus do we just get hit by.
This is the power of higher order of functions,
all right?
You can very quickly build yourself up a library
for doing higher level manipulations of things
and then re-use it very, very, very quickly.
Onward.
Okay.
So, we've talked a lot about functions, what
about data?
You got to have data in the program somewhere
so, of course, we're going to talk about structure
data we might as well about list and so, one
could use to define list like this.
This is actually a user defined type, this
is not the built in list.
It's just a list.
If you come from a certain persuasion, you
might choose to call that mill and cons instead
of end of list and link but let's just read
this.
When you have a piece of data of type of type--yes?
>> You work [INDISTINCT] do you actually type
what you're [INDISTINCT] into a compiler?
>> LENTCZNER: Either.
Good question.
So yes, I actually wrote the alphas because
I just wanted to flex that Haskell was actually
define in terms of unit code yohoo.
And so, you can--welcome to use Greek and
variable names if you wish.
Actually, in fact, you'd probably not unlikely
to see this, most people do in fact use Latin
letters in this particular context.
So, that is actually a tight variable, tight
variable.
So, this is--so, when you have a list of some
type, either note the or sign there.
Either it's just the end of the list, the
empty list or it is a link of the list with
some value of whatever the hell alpha ends
up being and then recursively another datum
of type list alpha, right?
This is just a standard kind of recursive
definition of the list.
Okay.
So, we can all play around with some obvious
types, where we can define the value empty
equals the end of the list.
Notice that we can kind of use these user
defined options, we call them constructors--don't
think constructors like C++ or java.
Empty is end of list, one word is a link of
the string apple and the end of the list,
right?
So that's a one word list, two words is a
link banana and then in parenthesis I create
another link of cantaloupe of end of list,
right?
Kind of sort it should be sort straight forward,
be brave, just go for it.
All right.
Pop quiz.
If we've given those three things I just typed,
what do you think happens with the bottom
with one of the first mysteries?
So, anyone guess, what is mystery number one?
It is a list of A pair.
It's one thing, right?
So, the first thing this is showing you is
that, all right--remember, there is no modification
of state in Haskell so when I declare the
empty equal to end of list, I can use it just
as well anywhere else.
You might think that's annoying because like
you can't modify state but it's actually really
nice because things factor really well, like
the guy who wrote mystery.1 is like, This
is a single element list, I'm certain of it,
you know.
Isn't a change [INDISTINCT] you think?
Mystery two, well, you can compose these things
link the string peach and then what?
And then some other list, right?
Some other item has to be in a list [INDISTINCT].
So, mystery two is a two word list, right?
Peach apple.
Mystery three, anyone would guess what mystery
three is?
Who wants to be brave?
>> Pineapples.
>> LENTCZNER: What?
>> Lots pineapple.
>> LENTCZNER: A lots of pineapples.
Yes, it's an infinite list of pineapples.
Haskell's perfectly happy with this, completely
happy.
Like you type it in, Haskell's like, Sure,
no problem.
It works fine.
Anyone want guess what mystery four is?
>> A tiger.
>> LENTCZNER: Yes, it's a tiger, all right.
Because something that's others that lists
are all right.
We said it was a list of some type and all
these other ones have been a list of string.
This first link has an integer but the second
link has a string and if you looked at the
definition you'll notice that it had the same
alpha in both places.
Yes?
>> Okay.
[INDISTINCT] So, what is like, a context of
mystery?
>> LENTCZNER: What is the context of mystery?
These are type of the top level.
>> So, I mean, at a point where mystery [INDISTINCT]
>> LENTCZNER: Yes.
>> Okay.
>> LENTCZNER: Yes.
So, your names are all on top of the same.
It's a mutually reclusive set of definitions.
Yes, so you can refer to yourself.
And the reason that this hasn't go often to
infinity at this moment is it's lazy.
All right.
No one's asked for what happens after the
first link of the list so, we haven't bothered
to go look there and the fact that we will
eventually go look one step further is fine.
Okay.
So, yes, number four isa type error.
Okay, let's write some quick functions on
list.
Good, I'm doing great on time.
All right.
Here are some examples of how you write functions.
Notice list back here has two different kinds
a datum of type list can have one of two forms,
it can be the end of the list that's one form
or it can be a link, that's the other form.
So, strangely enough it is common that all
our functions on list will have two clauses,
yes?
>> Going back to the type error.
>> LENTCZNER: Yes.
>> Well, [INDISTINCT]
>> LENTCZNER: No.
Not of the type you are used to.
There are strange, weird, wonderful, odd things
which you will be tempted to use in your first
weeks of Haskell, you know, that let's you
do this, I really, really need a help, you
know, a heterogeneous type.
And then when you've used Haskell for six
months, you're like, I'm so foolish.
I've never needed that heterogeneous type
ever.
So--and this one you've got to be brave and
like trust that this sounds crazy, that you
can't do this but in fact you really won't
actually need it.
There are other ways to achieve what you're
trying to achieve which are actually more
powerful and more importantly safer.
Okay.
So, some functions.
Because list has two forms, a datum of type
list has two forms, almost all functions on
it will have two clauses, notice I have two
equations for drop one.
So, we can read this pretty straight forward.
Drop one takes a list of some type and I don't
know what type it is and returns me a list
of the same type.
If it has the form link first rest [INDISTINCT]
pattern match variables they're just variables
for what we'll be matching in those positions,
then it's just the rest of the list having
dropped the first link.
And what if drop one gets a list of type form
end of list, what should I do?
Well, I don't know.
We've decided that dropping one element off
an empty list it's just an empty list.
That seems like a nice safe thing to do.
All right, we'll write end of list.
And do you want to guess what happens if you
leave off that second equation?
It is probably like, Dude, you forgot one,
your missing out, right?
So, one aspect about Haskell is that it's
pretty easy to make sure that you'd caught
all the cases, all right.
>> So, it catches that before you actually
[INDISTINCT] use that.
>> LENTCZNER: It catches it at compile time.
>> [INDISTINCT]
>> LENTCZNER: Yes, at compile time, it's like,
You missed one.
Like if you really want to miss one, go turn
off this warning here but like we really don't
suggest you miss one.
Why would you--why would you not want to miss
one?
You don't want to miss one because--all right.
If you have all the cases covered, then you're
certain the function always has a well-defined
value.
In fact, this is kind of something subtle,
it takes a while to get used to.
If you have all the cases covered then you're
certain your function never crashes and I
mean, never in a much more serious sense than
the never that you are used to.
I mean, it never crashes.
It can't.
Like it has a well-defined value, right?
If you covered all the cases and the compiler
doesn't complain, all right, then the only
possible problem that the function could have--I
mean, it might not implement what you're trying
to do, right?
It might have a different function than your
computer different result.
All right.
The only thing it can really do is go often
to, you know, go off diverge, go off an infinite
computation.
That's the only real error that you could
have left.
It's just kind of fun.
Okay.
Here's another function called Just One.
It's a little weird, it takes a list, it returns
a list, if the list is just a link then we
return a list of just that the first item,
right?
And what do we do if there's nothing in the
list?
We just want to return, I don't know it's
got to return something or get that compiler
warning which yells at me.
So, I'll have it return an empty list.
Okay.
It seems kind of weird but there's a function
definition.
Okay.
Pull back to reality.
Actually Haskellers, you know, lists are really
common.
We don't like to type so much and so, this
is from the standard library.
I want to point out a little bit something
amazing here.
The syntax of the square brackets is actually
built into the language but the type list
isn't.
This is actually defined in a library file
who's source code on your installation, you
can actually go find and look and inspect.
In fact, you can copy them to your own modules
and create your own version.
Which is kind of astonishing, right?
I mean, it's actually built in the language.
There's nothing magical about how this works.
So, here's everything I just wrote but it's
written with this new notation; notice end
of list is written at this funny empty square,
you know, double square brackets and link
is written by an infix operator called Colin,
right?
So, Colin puts an item the A onto--and this
is kind of a funny list of A. Don't worry
about it too much.
The syntax of list looks pretty much like
what you'd expect, in fact, it's so much like
what you expect, what I wrote is Haskellers
don't even type that much.
And I flip back the words real quickly between
these two, wait--stop that.
There you go.
The only real deference is on these lines,
people like to write lists this way because
they don't like to write the silly Colin,
look at the Colin, right?
The string apple Colin linked on to the empty
list.
They don't like to write that, they like to
write apple, you know, inside the square brackets
it's purely syntactic sugar that's all.
And these are all the same functions and so
much for that.
Okay.
Two more standard things and then we'll get
to some code, more code, okay.
A string is just a list of characters, really.
Here it is.
And then there's this funny other type, a
datum of type maybe.
It has two clauses too just like a list had
two clauses but it's even simpler than list;
maybe is either a datum of type maybes, some
type--it's either nothing or just a single
value.
Seems kind of crazy a little minor thing,
right?
This is how you use it, like maybe if this
function takes--pick message gets maybe an
integer, right?
It has to have two clauses because there's
two forms, right?
So, we pick message, if it's just an N then
I'm going to construct this string.
Pick a number like N, you know, show us how
you turn a value into a string, you know,
display it.
And then pick message if the value was nothing,
I'm going to say a different message.
So, that's how you use it, just pattern matching
just like we did with lists.
So, we have this awkward function called just
one, right?
Remember I said like, what if the list is
empty, what should I do?
Like what should I return?
It's really awkward because I want the first
thing on the list but just one gives me the
first thing on the list but damn it, it's
inside another freaking list, right?
It's kind of annoying, right?
So, you might be tempted to write the bad
function at the bottom, in which case, you
would be bad because look at it, it says,
if the list is, here's some cool notation
stuff.
We like to--we don't like to type a lot as
Haskellers, right?
If a list consist of a value A and the under
bar is, whatever, I don't care.
It's a variable that matches but doesn't--no
one uses it.
Then it's just A, just the first thing on
the list but if it's the empty list, well,
I can't like what A would I return?
I mean, if A is integer, should I return zero?
I don't know, maybe zero is a useful value.
I mean, what are you going to return?
Well it turns out it's like nothing you can
return in this case and so you write error
and error is this magical built-in function
which, like, kills your program and crushes
it with a message.
Actually, it does if you end up trying to
use it but--so you don't want to use an error,
it's a bad thing.
So that's a very bad thing to have happened.
So this one, like, shuts the compiler up but
you end up using an error into your program.
So, here's a better way.
So, let's use maybe because the answer is
this function maybe has an answer, maybe it
doesn't.
All right?
And so, this is a much more clean way to right
this all right?
The first one on the list is maybe of something.
We have had just or nothing.
Okay.
Keep your eye on the code very carefully.
Here's a real function that finds the first
character after a star.
So, given a string maybe it's going to return
the character maybe [INDISTINCT].
Notice we're getting rid of nulls, there's
no, like, no nulls in this language all right?
Find after star.
Well, if I have a list of a character and
another character and then the rest of the
list then if that character equals the star,
let's return just D because we found it, we
have a character after a star else, let's
just recurs find after star.
We'll put D in front of the rest of the list
and call it on that list.
Let's have another clause, if all else fails--under
bar, this is a catch hall, catches everything,
then nothing, there is nothing after a star.
Got it?
Anyone confused on how that code works?
No.
Yes?
>> So, you have mentioned matching semantics
are likely to [INDISTINCT]
>> LENTCZNER: Yes.
So the question is, "Are the pattern matching
semantics in order?"
And the answer is yes.
They write--they trust in the order that you
write them.
None of this funny reordering or any of that
stuff it's, like, exact--just read it in order,
that's how they test it.
Makes it easy.
>> Well, how about just [INDISTINCT]
>> LENTCZNER: So, just is right back here.
There's this type called maybe, it's a data
of type might.
So, maybe it has two forms.
If you have a datum of maybe an integer, either
you have nothing or you have just an integer.
So, there's two forms.
Just like the list had two forms.
A [INDISTINCT] and a cons or end of list and
a link.
So, there's two forms.
So we can return either kind--all right, here
we could return two things.
Now, in this case you might be asking--I'm
not sure this question you're asking is just
acting like a function in this case; it's
acting like a constructive or building a value
of type maybe.
So, this function is going to return a datum
that has either two forms: just a character
or it's going to be nothing.
>> I think that [INDISTINCT] that when you
define a type and give a list of constructors
and...
>> LENTCZNER: No.
>> ...constructors all have the same name
as the type...
>> LENTCZNER: Yes.
>> ...has [INDISTINCT] and give it new names
for each form.
>> LENTCZNER: Yes.
Right.
Every form has to have a unique name, right?
And that's how we distinquish them and tell
them apart.
All right.
And in essence, you don't--in other language
the constructive constructors, you know, you
have--constructors are just different ways
of building up, you know, an instance of that
class but, you know, then you just have an
instance that class then are all the same.
This is sort of the other way around right?
When you build up and instance of maybe by
using the nothing constructor, it is different
in structure than the version that has just
the value.
It actually has a different internal structure.
>> So, [INDISTINCT]
>> LENTCZNER: No, no.
This is user define.
It's not--it actually it's in the library,
it's standard but this is just built out of
a language.
>> Okay.
The point when you [INDISTINCT]
>> LENTCZNER: Because you always need a tag--there's
tags in the front that identify.
Think of it as a discriminated union.
This is like a union of different struck and
you need to have a tag in the front so you
can tell which one you've got.
And you use that tag both when you build it
and you use that tag when you tear it apart.
When do we tear it apart?
Pick message here is tearing it apart, it
is looking at the union and saying, "Huh,
is this value of maybe have a just tag on
it?
Oh, then of course it's got a value A that
follows the just tag."
Right?
And the bottom one says, "Oh, does this value
of maybe have just a nothing tag on it?"
Well, of course it doesn't have anything else
with nothing; all it's got is nothing.
Nada.
Okay.
So, here's the version that does string just
find after star.
And let me change it to find after char, I'm
going to make it more generic, I'm going to
give it a argument.
It takes another argument, a character of
[INDISTINCT] M to match.
And so all I did is instead of saying C double
equals--all right, so I gave an extra argument
M, you can see it up here, there it is.
All right, you can see that it does now compares
against M instead of against the fix character
star, I changed the tag signature, takes a
character and a string and returns maybe a
character so it finds after this character.
I had to add another thing here for nothing
but--otherwise, the code is basically the
same.
Oh, and when I recalled recursively, I had
a pass M again.
Okay.
I'm going to make this once more generic.
So, this function, I made it a little more
generic by checking for any character.
Let's make this a template and make it work
for any type not just strings.
Ready?
One, two, three, go.
Anyone noticed the only thing that changed?
Okay.
The name of the function, find after elem
instead of find after char.
The only thing that changed here is the type
declaration.
That's it.
Now, I did add this very funny weird piece
of cryptic nonsense, be brave, you know, trust
me it's good.
All it says is that A is a type that knows
how to do equality, that's all that's saying.
But instead of going from char to string,
remember string is just a list of char to
maybe char, I just made this generic.
Go from some type A and a list of As and maybe
A. Notice that the code is the same.
The actual body of the code is identical.
Yes?
>> So, is the type line necessary?
>> LENTCZNER: Boy, you're like the, you know,
like [INDISTINCT] man from the question.
Yes, so Mark, what is the deal here, like
what--like what is the type line?
The answer is, when we wrote this, the compiler
compile the bottom code and went, "Wow, this
is a nice cool generic function.
It finds any element after any other elements
in a given list.
Oh man, he wrote this restrictive types signature,
oh well, whatever fine, I'll only let him
use it in context of chars and strings."
When I wrote this, the compiler did the same
thing.
It read--looked at the exact same code, came
to the same exact conclusion and said, "Ha,
his type-line is right.
That's exactly right.
That's the type of that code.
This code is--this code will work if and only
if A is a type that understands equality."
It figured that out because I used the variable
here.
And it figured out that indeed there better
be a list because I used the list constructor
here.
And it figured out that the result better
be .maybe because I used Just and Nothing.
It figured all that automatically.
That's all it takes to make your code generic.
It is really common when writing Haskell to
suddenly realize that the thing you just wrote
for some particular case is actually complete
generic and you just fix the type line and
go on.
>> So you then--when you define string as
an array of char, you also...
>> LENTCZNER: List of char.
>> Sorry.
You also manually specify the Maybe and the
Just and the Nothing types in terms of that.
>> LENTCZNER: No, no, no.
>> [INDISTINCT]
>> LENTCZNER: You don't have to achieve or
write that.
That's all inferred.
Maybe was written--so when we were at maybe,
right so this is just like, well.
Okay, so what happened like, there's Maybe
int and Maybe char, but I just wrote this
thing called Maybe A. No, that A didn't mean,
oh yeah, when you get around to it, like,
type a version with replacing A, that really
is a type variable.
So you write that line, and Maybe is now defined
for every type: past, present, and future.
And it's already [INDISTINCT].
Okay, onward.
Now, Maybe, actually, is really important.
Maybe is so important that Maybe is actually
the thing that blew my mind.
This silly little teeny tiny itsy bitsy type,
this was like the first thing that, like,
[INDISTINCT] my first two weeks back in [INDISTINCT]
I was like, "Oh, okay."
This is thing is really useful, okay?
These functions--I just wrote there type signatures.
And you don't have to really pay too much
attention to them because just look at the
names, these are all functions you've used
in libraries all the time.
"Find the index of an element."
"Look up something on app," right?
"Shoot this prefix from this string," you
know.
"Get the port out of URI."
Anyone every written that one?
Okay, these are all functions which may not
have an answer.
And in almost every language and in every
library I have ever seen, gee whiz, we write
conventions like, "If the element is not in
the list, return -1."
Anyone ever been bitten by that?
Ever?
Right?
That is a sucky design.
All right, that's just bad.
Look how easy it is when you have Maybe, you--because
you can declare precisely and so here is the
first aha that types in Haskell.
Types in C and C++, as we know, are for the
compiler to make sure it lays it out in memory
correctly.
I mean at least initially.
Types in Haskell are for you to communicate
with other programmers what you mean in a
really deep sense.
In a sense I think that is deeper than others.
All right, so these are all examples of functions
where Maybe is far better than returning Null.
Did Null mean it wasn't there?
Did Null mean it was an error?
I mean what is that Null?
Like, I don't know what that Null means.
Is the empty string an error?
Or did stripping the prefix from the string
return me the empty string because it was
only the prefix or did--you all see the errors
here, right?
Okay.
So let's look how you use this.
So it turns out this other fun stuff about
Maybe because once you have something of this
concept into the language or that you've been
building language, you can now start building
better utilities for using it, all right?
So for example, all right.
Here's a function called addaweek.day, it
comes from a library, right?
So we add day 7 to some day object and we
get, you know, one week later.
And imagine I've got some list of interesting
dates and we're going to use our first one
function which given a list returns Maybe
1, right, because if the list is empty, then
you get nothing.
So I want to find, all right, one week later
from that interesting date, I can't apply
addaweek to an interesting date.
Everyone see why I can't apply it directly,
right?
Addaweek takes a day, but interestingdate
has a maybe a day, right, because it might
be a nothing.
So how do you do that?
Now in most other languages, what we would
probably do at this point in our code is we
would write an If statement, right?
If date equals null then do this, else right.
Okay, and we would probably propagate that
null further up the line, if date equals null,
return null.
Else, add seven to the date, okay?
Here, we use fmap.
What is fmap?
Well, you know what map is, right?
Map is like a function, like, for example,
addaweek.
And map would apply it to a list of days,
adding a week to every single one.
If map, let's just take a function of days
and apply it to a maybe day.
And fmap says, "Well, if we just have a day
then we'll apply it to the inner value and
return just that.
And if we have a nothing, we'll just return
a nothing.
We'll propagate the nothing.
Fmap does exactly what we want.
So, furthermore, if you want to think about
this like--I'm sorry, have I lost anyone?
How fmap--what fmap does?
Fmap takes a function from A to A--actually
from A to B, but [INDISTINCT] on that.
In this case, it takes the addaweek function
which goes from day to day, and allows us
to apply that function inside--to the value
inside a Maybe.
If there's a Just in there, we'll apply it.
If there isn't then not.
Of course, we think of--yeah?
>> [INDISTINCT]
>> LENTCZNER: Well, it's in the library.
But it is user-defined, yes.
>> [INDISTINCT]
>> LENTCZNER: Ah, how does this--how does
it know how to apply the real value or not.
So here's an even bigger sort of--here's a
Simon for a true head explosion, how that
is done is not built into the language either.
That's in the library as well.
So you can go and find how fmap is implemented.
And you're like, "Oh my God, it can do that.
That's cool."
A little outside the talk, but...
>> [INDISTINCT]
>> LENTCZNER: That is true, right.
>> [INDISTINCT]
>> LENTCZNER: Yes, there is writing code for
that.
Although--yes.
So--all right, so fmap, in the same way that
I said, you could think of map as taking a
function from A to B and turning it into a
function from list of A to list of B, we're
going to give fmap, at least in this context,
as taking a function from A to B--or in this
case, day to day--and turning it into a function
from Maybe Day to Maybe Day.
Fmap is lifting your function up into this
other environment.
Okay, and so you can do a week later.
Now, the cool thing about this stuff is that
all the stuff is in the library.
And you can build things which end up being
really powerful.
Here's an operator.
It's got a funny spelling, greater than vertical
barl, less than--greater than the vertical
barl, less than, whatever.
You can define your own operators out of symbols
and Haskell Amuse-Bouche, people sometimes
do.
This function don't pay too much attention
to it.
It does exactly what you think it does.
It is short-circuit evaluation on--based on
maybe.
It runs the first function, favorite show,
which returns maybe a string if the person
has a favorite show, or it returns show with
name, right?
And it does show with [INDISTINCT] so it takes--by
four it takes two maybes and if the first
one is just a value, it returns that.
If it's nothing, then it returns to the second
one.
It's short-circuit choice.
The cool thing about this is, this is not--right--now,
we all work in languages with a short-circuit
choice, but they're generally built in and
you cannot build user operators to do the
same thing, or user functions that do same
thing.
Must you're working in common less when you've
got some--or scheme and you've got some great
macro system, right.
But, you know, most of us are stuck with,
you know, C++.
Where you can't really do this, it turns out
you can make your own functions do this kind
of stuff all the time because it is lazy.
And so, the reality is that, any of these
operations can be extremely complexed to compute,
but because the Or operator will never, you
know, if your operator never selects that
option, and never returns it, the computation
never actually gets done.
So, that's kind of fun.
Here's another common thing we often do, all
right.
So this would've been a series of ifs have
we--have to write this was no, as the key
value as the value indicating that there was
no answer.
[INDISTINCT] so this is much more concise,
all right.
Okay.
Here's another thing we do, so here's a set
of functions that, like, get headered, it
takes a string and a message and maybe gets
a string because there might not be a header.
And parse state gets a string and maybe returns
to a day because maybe it isn't parseable.
And mailbox for a day, it takes a date and
maybe we have a mailbox and the user's file
system for that date or something, you know,
who knows what that function does but they're
all valued to maybe.
And so what I wanted to do is, I want to pass
the values along stopping at any point in
the sequence in which I get nothing.
So get header for a day, that answer is nothing,
we're done we return a nothing.
Otherwise, I need to sort of unwrap the just
value and pass it to the next function, parse
state which if it succeeds in parsing that
header as a date, then I want to unwrap that
maybe to just get the date value out and pass
that to mailbox, all right.
So we would normally write this as a giant
cascade of ifs, a nested ifs all the way at
the bottom.
And the else [INDISTINCT] to the whole--either
we'd return from the inner part or worse,
if we had to continue to use the value we'd
have to set some value.
It's the default value at the top or no, and
do the [INDISTINCT] you know, it would be
some horrible nested thing.
And so, we have this kind of injection operator,
it's pronounced "bind".
Which is does that.
Now, I won't give quite too in there but it's
get a little bit to the question about like,
how does it know how to do F map.
So, it turns out that these functions, these
things, these operators are actually part
of type classes which I'm not going to explain
too much.
But, are wonderfully useful and the answers
is that they're highly generic so it turns
out we can use F map actually over lists.
We can use that alternative thing with lists,
we can use it with--we can use bind with things
that are monads.
These things are highly generic and get used
for lots and lots of it's [INDISTINCT] places.
Okay.
Do we have time for just one more?
Yes, we have 11 minutes so, go.
Now, the thing I really love about Haskell,
one of things I really love.
It's a strongly type language, that types
really help you encode what you mean to say
to your fellow programmers, the types the
compiler will really catch your ass when forget
at light, oops, you didn't cover the empty
case, you know, it's really nice.
But here's the thing I like most about the
types in Haskell, you don't actually type
them very much.
Okay.
Here is a wrong lengthing code function, I
will not through how it works, you can work
that out.
It's a fun little puzzle.
Well, it's not really a puzzle, just a fun
little piece of code.
The only type declarations are on the very
first line, it says if A is a type that knows
how to do equality, then given a list of A,
I will return a list of pairs of A and an
integer right, this is standard wrong lengthing
code.
Okay.
And blah, blah, blah.
And now I've got--I'm aware lets me introduce
like local functions, these are just local
functions that I'm only using inside this
other function that's where the ware clause
is.
I've got local variables, I've got this crazy
function called next group which seems to
take one, two, three arguments, I think.
You know, it's got tests, it's got all the
stuff in here.
The only types I wrote were up at this top
line.
And yet, right.
The type of every single thing on the rest
of this function has been completely inferred
by the compiler, checked, verified.
And I get all that protections for all those
things even though I didn't bother to ever
write their types.
All right.
This is like the--so this is just like [INDISTINCT]
it was like, wait a minute, it's all the benefits
of type checking or even more benefits of
type checking but I write it like I used to
write python or I didn't write types anywhere,
like, oh, that's nice.
I don't have to write all those things.
Now, C++, x11, 0x11, whatever it's called
these days has auto, the auto type which is
somewhat like this, although I don't believe
it's quite as powerful or quite as hubicueous.
And, you know, there are languages that we
use that sometimes have types or not, or let
you--have things un-typed.
This is great.
This is types that you don't type-checking
but you don't ever type them.
Now, let's compare that to C++, are you ready?
That's fun.
There are a lot of types here, and what bothers
me most is the programmer is.
There are a lot of types that are repeated
a lot.
Okay.
So first of, I have to tell, see something
that this is generic, okay, whatever.
Now, I have to decide list of a pair of Ts
and Ns, okay that's a lot more wordy than
the way you write then in Haskell but I had
to write that on Haskell.
And it takes as an argument a list of Ts,
I had to write that in Haskell too, we'll
ignore the constany.
Then I have this local variable and crap,
I had to duplicate the freaking type again
and I had to do that.
And then, here's the one I really love.
I have an etorator but I like that I have
to type, type to tell the compiler that the
next thing I type is a type.
Right?
I mean, this is really bloody awesome.
Now, it's true, C+--the new C++ will you write
auto there in and will figure it out.
Yey, C++ learned something from Haskell or
other type inferencing systems.
But, there's a lot of type stuff you have
to write here and it's pain, and it's knarly,
and it's in the middle of all the damn code
and it annoys me.
By the way, just another minor little point,
the type systems has lots--it can lets people
do crazy wonderful things.
If you've never encountered a quick check
library, I don't know if Haskell was the first
quick check library but I think--and who knows.
But it's certainly--here's a bunch of properties
about that run length encoding function.
I just simply read them as functions from
a given input and to bullion and I expect
these all to be true.
Like, I expected the length of the list equals
the sum of the second elements of the run
lengthing coded lists.
Oops that's mine--sorry, that line is really
long, right.
And run length code the list, I take the second
elements of all the sum and I summed them
and I expect that to be the length of the
original list.
That's kind of, you know, a run length encoding
invariant.
And these are all various invariants that
I just I wrote.
Okay.
I would like to actually test this, so I could
got to my unit and test, frame, or can write,
you know, several hundred, you know, unit
tests so I could write--well, I probably write
maybe a dozen tests unit, you know, tests
cases for this by hand.
But in Haskell, because of the type system
I can use the thing called quick check and
I can ask quick check just to check this function
and quick check will derive from the type
of the function, the possible set of inputs,
like it could go there.
Then generate test cases and it's relatively
smart by generating test cases that actually
matter, like, you know, like, oh it's a list
I better try empty list that's got to be really
important.
I better try a list of just one item, you
know, it gets all the good corner cases.
And it generates cases so this actually allowed
me to run 300 test cases on my run length
encoding function.
You can actually, really, honest to god, like,
pull this in, type it and run it.
This is the entire--sorry, that plus this,
plus the loading the module quick check is
all it takes to be able to run this.
There was no actual additional scaffold thing
I've left out.
Okay.
So, that's actually really sort of fun.
Okay.
So a few other things to point out about the
Haskell ecosystem, we've gone to the language
a lot but the ecology of Haskell is actually
tremendously nice to work with.
GHC, the Glasgow Haskell--the glorious Glasgow
Haskell Compiler is the--probably premier
standard and the most common used compiler
now.
It has a command line ripple called GHCI the
interactive version which is just a delight
to use.
It's really trivial load all where all these
code up in there and reload your code and
you can ask if things like, I wrote this code
and I didn't write a type signature, can you
tell me what the compiler thinks, the type
of this code is?
That's actually really common for Haskell
programmers to do.
I don't want to figure out what it is, you
just tell me.
Cabal and Hackage, Cabal is a packaging and
building subsystem and Hackage is a very large
package repository of now about 3000 packages.
Cabal is the nicest package manager I personally
have ever used.
It's the only one I know that knows how to
install things locally just for you by default.
You know, it can do system installs or personal
installs and it does all the recursive dependency
analysis and download and configure and reinstall
those things and it's just enjoy to use.
Hatek is the inline documentation system which
I worked on so, recently so it--but it's nice
and it looks cool.
Hugo is astonishing.
Do I have three seconds to show you Hugo?
I do.
Okay.
Oops, that's what I want to do.
All right.
Hugo again, the power of types, Hugo is a
search engine for code that you've never seen
the--just--let's say I have a function, I
need to go from maybe some value and the list
of values and I want to end up with the list
of values.
Is there a function that does that?
Oh, yeah there is.
So, you can actually--or you can see like,
what's a better example?
Like, I've got a--I've got a string I'm looking
for something that's like a, I don't know.
You know, what manipulates two string, you
know.
You can actually search by type signature
and find all sorts of wonderful functions
that you need.
It's astonishing how quickly it is to find
what you need.
And the search is not just what's--the search
is like all the packages and stuff, so.
Hugo is very wonderful and great.
It is worth noting that--I think it's a very
recently hash Haskell and freenode was the
largest room, I see it has typically 6700
people on it.
It's a very friendly community and it's a
really great place to get your questions answered
anytime of day or night, just kind of fun.
Want to learn more?
Go Haskell internally, we'll get you a bunch
of stuff including some of these links as
well.
Haskell is actually used here at Goggle and
if you select secret corners find out more
there.
What else?
Learn new Has--these are some great online
tutorials and stuff and, we're done, thanks.
I have two minutes for questions.
>> What does it look like when you write something
[INDISTINCT]
>> LENTCZNER: So, yes.
What does it look like when you write big
table or some big giant server, and is it
this pretty?
The answer is, yeah.
Lots of it are, so one of the nice things
about Haskell is it's very easy to cleanly
separate that parts of your code, it's really
good and [INDISTINCT] is modularity and reuses
very, very high and so often is really easy
to take the parts of your code that knarly
in dealing with the, you know, with the network
stack in dealing with, you know, the vagaries
of--well, like [INDISTINCT] back in web servers
and Haskell that do standard stuff talking
on my sequel.
And it's very, very easy to sort of incapsulate
because you could do always nice [INDISTINCT]
functions that deal with the IO stuff or deal
with the database stuff.
You can stick that on the scion and have like
your logic be nice--and your logic and your
business logic, if you want to call that back
in stuff really clearly separated out.
It's also the case that, because of the high
orderness and laziness often in libraries,
we have to--because we don't want the user
to actually execute user code if it's not
necessary, we all have to bubble control back
out to the user.
Users of libraries are generally have to do
the control operations Highlable control operations
and Haskell you can--you can bury all that
back down in your library so, the short answer
is, yeah it's a lot nicer.
In my experience writing the exact same server
in, well big server library in C++, python
and Haskell, the Haskell code was literally
one-third the size.
I can give you pointers that code's open source
if you want to actually see the comparison
of Haskell that's one-third of the C++ size.
It's about half the size of the python.
Did I say Haskell?
That's like, python.
Yes, there's a question over here?
>> Actually, I want to know which office has
all the [INDISTINCT]
>> LENTCZNER: Yeah, who's the big office is
that?
>> Big or [INDISTINCT]
>> LENTCZNER: Oh cool, wow.
Hi New Yorkers.
Great.
Anything else?
>> [INDISTINCT]
>> LENTCZNER: I can answer that question offline.
>> [INDISTINCT] build system, is the build
system supported?
>> LENTCZNER: Yes, it's in the, yeah.
Yeah, out build systems had stuff in it.
Blaze has support for Haskell.
>> What kind of [INDISTINCT]
>> LENTCZNER: Well, I don't like to write
client web front end code because I can't
get the damn Haskell to run in time.
The processor on the front end.
I would in an instant.
You know, in my mind Haskell has, of the last
couple years finally traversed into the realm
of like, yup.
It's a good general purpose programming language
enough functionality is to almost any kind
of stuff that you need.
I don't know, I'm too much of a sand to answer
that question.
My answer is I do everything in Haskell if
can.
All right.
I can stick around and we're officially over
and we can stop the recording, thank you.
And I'll stick around if anyone wants to talk
or go over or find a...
