
Programming With Types, Not Tutorials - mightybyte
https://www.fpcomplete.com/user/chowells79/types-not-tutorials#desk-darkness
======
mightybyte
This article gives a nice glimpse into the essence of why modern strong static
type systems like Haskell's are such a huge win for development. The type
system gives you a way to bound the number of things that you must understand
about an API in order to use it. The confidence that we get from having these
guarantees makes code reuse orders of magnitude easier in Haskell than it is
in pretty much any other language in mainstream use today.

All this reminds me of a comment made by a friend of mine awhile back when he
was learning Haskell. He said that programming in Haskell feels less like
programming and more like putting together a puzzle. I think this article very
nicely captures what he was getting at.

~~~
siddboots
> ... like putting together a puzzle

It's funny how lots of good formalisms, notations, and DSLs all achieve this
quality in different ways.

In particular, this reminds me of the mathematics of tensors, or Bra-Ket
notation in QM. Both of these systems use notation to give each piece of the
puzzle a particular _shape_ , so that if you know all of the pieces you can
often see immediately how they should fit together.

This seems (to me) to be the most important part of designing an API, and the
brilliant strength of a good type system is that you get this quality for
free.

~~~
eru
> [...] get this quality for free.

Let's say, for much cheaper. Designing a good API in Haskell still requires
work---but it's arguably easier to get right than in most other languages.

------
captainmuon
I honestly have no idea what all of that does. I'm sure I could work my way
through it, but still... in other languages you can get a glimpse of the
purpose of a program even if you don't know the syntax. You can tell whether
it is a parser, a game, a Hello World, or a web application.

The other thing I dislike besides the opaqueness of Haskell is what another
commenter described as "mechanically filling in" of code. I get that feeling
every time I try strongly typed functional languages. I had the same feeling
when I started with C++ and stuggled with `const`. I had an idea in my head,
but couldn't convert it to code because the compiler didn't allow it. Instead
of helping me, the syntax slowed me down, took me out of the "flow". It
rarely, if ever, helped me avoid bugs. Now, I've probably learned avoidance
strategies, and rarely have problems with `const` anymore. Haskell gives me
that old feeling of programming in a straightjacket, multiplied by 1000.

~~~
nbouscal
If you don't know functional programming, of course you won't be able to
understand functional code at a glance. You wouldn't expect someone who
doesn't know imperative programming to intuitively understand imperative code
at a glance either. It's not opaque, it just requires learning.

Not being able to convert the code in your head into type-safe functioning
code just means that you don't understand the code well enough yet. That's not
a bad thing about you or about the language, it's just the state of things.
Once you gain that understanding, you'll be able to write code that compiles,
and you'll also discover that now the compiler is your best friend. Writing
Haskell is extremely comforting to me, because I know that the compiler has my
back. It's not a straightjacket, it's a guardrail alongside a mountain
highway.

~~~
hcarvalhoalves
I don't think it's about functional programming, not even type systems. It's
about all the idiosyncrasies Haskell throws at the programmer. Even the author
adds in the end:

> I still don't really understand free. I definitely don't have a clue what
> kan-extensions are in general, or what Coyoneda is specifically.

So, he got a working program (because it's assumed it works if it compiles),
but can't explain why, what the components are or do. I'm sure there are a
plethora of strong points about Haskell, but his conclusion that you can just
gloss over it is not making a good case for it in my opinion, it's actually a
huge red flag.

~~~
nbouscal
I think you misunderstood the article. He knows exactly what the components
allow him to accomplish, he just doesn't know how exactly they work or any of
the math behind them. This is directly analogous to the situation in every
other programming language: very few programmers know exactly how every
library that they use works. This is good, because in practice there isn't
time to learn the intricacies of every one of your dependencies.

To be clear, this also has very little to do with Haskell. kan-extensions is a
library, it's not a part of Haskell itself. His point is that if you had a
kan-extensions package in some other language, it would be a hell of a lot
more difficult to use it in your program without learning the math yourself.
And again, the key point here isn't that you can gloss over what the library
allows you to do, it's that you can gloss over the complex abstract
mathematics that went into the creation of the library. You can get the
benefits of free monads without taking a course in category theory. That's
definitely a good thing.

------
bcjordan
Sort of off-topic, but wow, this is a really neat presentation format for
tutorials. Love the markdown source[0] and the "open snippet in IDE" features.

[0]: [https://www.fpcomplete.com/tutorial-
raw/5133/cf0de9edd526ea5...](https://www.fpcomplete.com/tutorial-
raw/5133/cf0de9edd526ea54e3b79358039b3090fbfddd37)

------
voyou
Is this a good thing? What the development process in this post appears to
involve is a mostly-mechanical filling in of function definitions to match a
pre-given type signature; but, if it's mechanical, shouldn't the compiler be
able to do it for us?

~~~
astrobe_
Haskell is absolutely fantastic. In C, when I'm given only the prototype of a
function like:

> int foo(int, int);

Even if you tell me it is referentially transparent, I have no idea what that
bloody function really does.

But with HASKELL, with just the type signature you can write a program that
does what you want even though you don't know what the components you used do!
Mind/Blown.

~~~
BSousa
I Love Haskell but I think sometimes people give it more credit than deserved.
So you say

> int foo(int, int);

Doesn't tell you anything. Ok, you are right! But that code translated to
Haskell would be:

> foo :: Int -> Int -> Int

How does that give you more information about foo than the C code above?

Now if you say you use type synonyms in Haskell, I would agree, but you can
use typedefs in C as well.

~~~
nightski
Well it does tell you that there are no IO computations with side effects. So
you really are restricted to the Int type and the operations that can be
performed on it.

You get much stronger statements by using type variables (not type synonyms).
This is actually laid out rather clearly in Theorems for Free by Wadler[1].
The essence is that the more generic a type signature gets, the more you can
reason about it's behavior. Since if you want to perform (+) (-) (*) (/) for
example you'll need to include a (Num) constraint.

[1]
[http://ttic.uchicago.edu/~dreyer/course/papers/wadler.pdf](http://ttic.uchicago.edu/~dreyer/course/papers/wadler.pdf)

~~~
jlarocco
> So you really are restricted to the Int type and the operations that can be
> performed on it.

That's so vague it's almost meaningless, and doesn't support the original
assertion, which was "But with HASKELL, with just the type signature you can
write a program that does what you want even though you don't know what the
components you used do!"

Even given a simple thing like foo:: int -> int -> int, there's really no way
to tell what it's doing. It could be multiplying the arguments together,
dividing, adding, computing psuedo-random numbers using two seed values, ...

Not only that, but I'm not sure it's true that it's limited to the Int type
because there are things like System.IO.Unsafe.

~~~
mightybyte
> Even given a simple thing like foo:: int -> int -> int, there's really no
> way to tell what it's doing.

Ahh, this is an important point. The reason you can't tell what it's doing is
because it is quite concrete. One really cool thing about this kind of type
system is that the more generic your functions, the more you can tell about
what it's doing. Consider this function:

    
    
        foo :: (a, b) -> a
    

There's only one possible thing this function can do! And there's only one
possible implementation--the correct one. This is the case because the types
involved are completely general. Type signatures can communicate a lot more
than one might think.

~~~
jlarocco
That's all well and good with a trivial function like foo :: (a,b) -> a, but
for most non-trivial functions it won't be so obvious, and that's exactly when
it's most helpful to know what's going on.

~~~
nightski
a.) Many functions in Haskell are this trivial. map, fold, etc...

b.) Functions that are non-trivial will have additional constraints often
hinting exactly at what the function does.

Most important is not the fact that you can write poor Haskell code which does
not exibit this self-documenting behavior, but rather that if you choose to
exploit the type system it has the expressiveness to allow you to do so! (and
enforce it)

If you are working with a Haskell library and see a function with the
signature (IO Int) you may start to question your choice of library :)

The only problem is, once you get a taste of this power it is easy to yearn
for even more such as dependent types (i.e. Agda, Idris...)

------
Kluny
I'm just starting to learn Haskell, reading "Learn You A Haskell..." I didn't
realize that Haskell documentation isn't considered very good. Does HN agree
with that? Are there any must read documents besides the official ones and
Learn You A Haskell to mitigate that problem?

~~~
tel
A lot of intermediate Haskell material (and advanced Haskell material) is
locked up in a series of papers. They're usually well-written, but the
intermediate Haskell canon is not well-defined nor is it in a comfortable
format; I often find that when I recommend a paper to someone they have a
tendency to be turned away... even if it's a tutorial paper not so different
from a blog post.

~~~
Buttons840
If someone started a blog where they give an approachable overview of all
these papers, would that be helpful? Would such a blog be considered rude
since many people would read the blog summary instead of the actual paper?

~~~
klrr
Okey, this is a question to the parent of the comment I reply to. (I couldn't
reply to tel for some reason).

Do you have any example were the documentation available is only scientific or
technical papers? I've been learning Haskell for a year now, and even though
I've read some papers due to interest I've never felt like it was the only
form of documentation.

~~~
tel
I'd say that people often reference intermediate Haskellers to a number of
major papers like Data Types A La Carte or the Typeclassopedia. The entire set
of Functional Pearls are released in academic paper form and provide a really
wonderful glimpse into "advanced" Haskell style. You also would probably want
to take a look at many of the things that Jeffrey Gibbons or Oleg Kiselyov
have written.

I could think of others if I dedicated some time to it, but those come up
fairly frequently.

------
tree_of_item
This is a rather frightening article. Why is it considered a good thing to not
know anything about the components that make up your program? This seems like
blackbox abstraction taken to its absurd conclusion, where programmers simply
wire together type compatible components without any idea of what they're
actually doing.

What does the example program even do?

I think this article is one of the best explanations of the visceral reaction
some programmers have to Haskell. There's a _lot_ of theory behind Haskell's
semantics and libraries, and some people just do not enjoy using something
without having any clue of how it works or how it's implemented. Even a hand-
wavy naturalistic explanation is better than a dismissive "you don't have to
worry about that".

~~~
wting
Are you implying developers should know the inner details of every library
used?

Realistically most devs read documentation or Google to get something done.
They only dive into the library if the documentation is unclear or something
is broken. Reading well written libraries is a good learning experience, but
not a requisite to use one.

In Haskell a function's type signature is expressive enough to exist as
rudimentary API documentation.

------
jlarocco
It seems to me that this article actually proves the opposite point that it's
trying to make.

I have no idea what the program presented is supposed to do, or what problem
is being solved, despite knowing all the types being used.

I swear the point of Haskell is to make people using Haskell feel smart :-)

~~~
Guvante
A simpler example would be a basic list operator, given a method you can guess
what it does.

    
    
        [a] -> a
    

That takes a list (brackets denote a linked list) and returns a single item.
There is a small list of things that this could be. It can't filter or order
in any way, since there are no restrictions or functions provided, it can't
aggregate for the same reason, that leaves either the first item or the last
item.

[Yep head and
last]([http://www.haskell.org/hoogle/?hoogle=%5Ba%5D+-%3E+a](http://www.haskell.org/hoogle/?hoogle=%5Ba%5D+-%3E+a))

Looking at that list we can see some other alternatives that I mentioned

    
    
        foldl1 :: (a -> a -> a) -> [a] -> a --Aggregation
        maximumBy :: (a -> a -> Ordering) -> [a] -> a --Ordering
        maximum :: Ord a => [a] -> a
        (!!) :: [a] -> Int -> a --Indexing
        sum :: Num a => [a] -> a --Aggregating
        product :: Num a => [a] -> a
    

And if followed along, you will notice all of his implementation points were
straightforward.

Note that all of this assumes you have very well written APIs that follow this
chain of logic, otherwise you can write things like this.

    
    
        second :: [a] -> a
        second _:i:_ = i
    

It compiles (or would with the default case added) but is rather odd and would
throw off this line of reasoning. Similarly by abusing unsafe methods you can
do crazy things.

~~~
nandemo
Your function second is no worse than head or last. I think you chose a
difficult example. [a]->[a] or a->a would be simpler, if a bit boring.

~~~
Guvante
I dunno, I like second being a bad choice, the number of times it is useful
are very few. `!! 2` is much more straightforward. In contrast `last` is
impossible to do that way and `head` is part of the underlying data structure
(as in it is a linked list, so has head and tail) so is a gimme.

------
skywhopper
Pretty depressing if you ask me. You're limited to just plugging libraries
together if all you understand is the type system. Hey it compiles and runs
without error. Congrats. But does it do what you want? Impossible to say.

~~~
bmurphy1976
It takes time man. Understanding is a process. You have to start somewhere.
Good for him, it was all greek to me.

------
pekk
The idea that you don't need tutorials because of the type system is absurd,
another whopper along the same lines as Haskell programs being bug-free.

If nothing else, you may need a tutorial to learn to use the complex type
system.

~~~
chowells
That's why I was very precise in my statement.

    
    
        The great thing about Haskell is that for many purposes, you don't need to understand the math or have a tutorial to use a new library.
    

and

    
    
        But a complaint that you can't use most libraries because they involve math you don't understand and don't have any tutorials? That's one of the silliest things I've ever heard.
    

Note the word "library" in both of those. I was very precise. Library
documentation isn't lacking the way some people complain loudly that it is.

~~~
eru
Yes. More documentation wouldn't hurt, though. The type system is more
expressive than most other languages', but not that expressive on an absolute
scale.

------
mattdw
My issue with this is that, quite aside from _my_ lack of understanding of
what's going on, the author is constantly professing their lack of
understanding of the underlying details.

> _I still don 't know a thing about what F or Coyoneda mean. But it isn't
> really slowing me down._

They're essentially cargo-culting signatures, and while sometimes that can be
enough to stimulate proper understanding, I worry that the first time
something goes wrong the author will be hard-pressed to solve it. It seems
like a useful way to stumble around in unexplored areas; it doesn't seem like
a good way to build software you're going to have to maintain and support.

~~~
the_af
I couldn't maintain or understand the low-level code Java depends on without
great effort (I know I could if I devoted enough time, which I usually don't
have). Same for low-level libraries for C or any other language.

I think the argument here is that -- given that there are no problems with the
library; otherwise you're screwed in any language -- type information for
Haskell libraries gives you _more useful information_ than docs & tutorials
for libraries in other languages.

The author repeatedly asserts that he is not scared of math, and would
probably be able to understand the libraries he uses if he spent the time.
It's just that he doesn't _need_ to.

------
mark_l_watson
bcjordan: sorry, I meant to up vote and my thumb missed, hitting the down
arrow.

fpcomplete is a nice system for learning. I like the live code and the IDE
mode. I signed up for a year paid account, about two weeks ago.

------
CharlesMerriam2
As a long time programmer, this gives an insight into why I avoid Haskell.
Syntax matters, in part because it has such an influence on style. When I look
at the code, I can't read it. There seem to be poor choices on
choosingcasesformultiplewords that dependOnContext but are still unreadable.

I can't help feeling that the reserved terms, such as Functor, Applicative, F,
and liftF bring the chaos to the thinking process that is reflected in the
article. I skimmed it, tried to read it, and still have no idea what the
person was even trying to do.

This is a language that discourages clarity.

~~~
awj
Where are you seeing any discrepancies from camelCase? The only thing I see is
that types and constructors (i.e. Functor/Applicative/F) start with capital
letters.

Also, to be frank, many languages give a false sense of knowledge portability.
"As a long time programmer" may not carry you so far into Haskell (or Prolog,
or Forth) unless you've seen something else in their paradigms.

~~~
freyrs3
> Also, to be frank, many languages give a false sense of knowledge
> portability.

This is true, there's a very common psychological bias that people experienced
in one domain when encountering a new subject which they find difficult will
sometimes blame the new subject matter as flawed or overly complicated instead
of realizing the limitations of their own knowledge.

