|
![](/i/fill.gif) |
>> The hard thing about writing a tutorial is that by the time you're
>> experienced enough to be an authority on the language, half the stuff
>> that trips newbies up is so utterly "obvious" to you that you forget
>> to even mention it.
>
> Exactly.
This is one of the hardest things about technical writing - and one of
the things I most often see done wrong. Search around the Internet a
bit, and you'll find Haskellers writing things like "a monad is just a
monoid in the category of endofunctors - what's the problem?" like
normal human beings have any clue WTF that means. (I have *some* clue,
but the category theory definition of a monad doesn't even begin to
explain why it's useful in computer programming.)
>> In truth, if you're using tuples bigger than about three or four
>> elements, you really ought to define a custom type for this stuff
>> instead of relying on tuples.
>
> OK. I take it a custom type is a bit like a struct in C-type languages,
> in that it can hold any number of different types?
It can be. The neat thing about Haskell is that it has "algebraic data
types". These can be like a struct or a record or whatever you want to
call it. Or they can be like an enumeration. OR THEY CAN BE BOTH!
(I guess this is what C would call a "union". But it's type-safe. You
cannot accidentally access a field that does not exist. Or use the wrong
field type.)
>> I can talk some more if you want?
>
> Once you know a few basic things I think it's best to move on to simple
> examples (small multi-line samples, not those artificial ones needed for
> the web).
Some things are easiest to explain by example. Like, some concepts are
so extremely abstract that without an example, it's difficult to
comprehend what's going on. Other things are quite tricky to demonstrate
with examples; you end up needing to use examples which are either
extremely complex, or very contrived. In depends what you're trying to
demonstrate.
I think for general familiarity with a programming language, lots of
small examples is probably the way forward.
>> > let factorial n = product [1..n]
>> > let choose n k = (factorial n) / (factorial k * factorial (n - k))
>> > choose 3 5
>
> Exactly. Even I can work out from that one how functions are defined
> now :-)
Well, yeah, there is that too. ;-)
> BTW what would happen if I typed "choose 3 (-5)" instead?
Well, it would execute
(factorial 3) / (factorial (-5) * factorial (3 + 5))
Given the above definition of factorial,
factorial (-5) = product [1..-5] = product [] = 1
Which I'm guessing means that choose gives the wrong answer.
(I also just realised that that should probably be integer division,
since choose is only supposed to work with integer inputs and yield
integer outputs...)
> I'm just trying to think of a subject for a good example ... how about a
> card game? Firstly how would you represent the cards (tuple or custom
> type for the suit/value?).
At the most basic level, you can always use tuples and code numbers. But
why do that, when the language has extensive support for a better way?
The idiomatic way would probably be to do something like
data Suit = Diamonds | Hearts | Clubs | Spades
data Number =
Ace | Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten |
Jack | Queen | King
data Card = Card Suit Number
So Suit and Number are enumerations, and Card is a record with two
fields. The record syntax is probably the most perplexing part. I'm
using positional fields - which is common when there are very few fields
- but you can also use named fields:
data Card = Card {suit :: Suit, numer :: Numer}
I should probably have thrown it into the tutorial, but "thing :: type"
is how you write explicit type signatures in Haskell. And you can do
> 5 :: Int
> 5 :: Double
> 5 :: Rational
> 5 :: Word64
> 5 :: Complex Float
Unlike Java, "5" can represent any possible numeric type. Usually
Haskell deduces which type you want automatically (or makes it
polymorphic). The web interpretter seems to default to
arbitrary-precision integers - which is reasonable.
(Similarly, "5.3" is any fractional type - Float, Double, Rational, etc.)
> Then I assume you would just create a list
> of "Cards" to represent a group of cards that someone was holding (say 5
> cards).
Sure, that'll work. Other representations are possible.
> How would you then check to see if any two cards in that list
> were the same value, or all the same suit, etc?
By default, you can't compare values. That's because it's possible to
define how to do comparison. (E.g., your data structure might have
several equivilent representations for the same thing, and you want
these different representations to be considered "the same".) However,
in the common case that you just want to do a vanilla structural
comparison, you just add "deriving Eq" to the end of the type definition.
Once you do that, you can write things like Hearts == Clubs (which is
obviously going to give you "false"). (By a similar arrangement, you can
auto-define comparison as well. The compiler uses the order you listed
the values in. Might be useful for deciding which card is "higher".)
Now, if you've used named fields, then "suit" and "number" are not only
field names, but also implicitly-defined functions that take a Card and
return the appropriate field. So "suit card1" tells you the suit of
whatever is in card1.
Are all the cards the same suit?
length (group (map suit cards)) == 1
(This could be inefficient if "cards" is huge, but for a hand of 5 or 7
cards, it should be fine.)
First, you extract a list of suits. Then you group this list into a list
of sublists, such that each sublist contains only identical suits. If
*all* the suits are the same, clearly you will have exactly 1 sublist.
(Equally clearly, if you have 1000 cards, alternating between Clubs and
Spades, you will get a list with 1000 1-element sublists, which length
will have to completely traverse before giving you an answer.)
You can use pattern matching to solve the inefficiency, but due to the
limitations of the web interpretter, it's bloody awkward to demonstrate
pattern matching. So I haven't shown that to you yet.
>> Are you actually interested enough to want to play with it more?
>
> I was interested enough in J to learn how to use it to write a
> mandelbrot in 1-line :-) Haskell should be a doddle - at least you
> stand to pick up bits and pieces based on English, rather than just
> almost random single characters for everything!
LOL! OK, well if you put it that way...
Although even Haskell has a few "interesting" symbol choices. ">>="
leaps to mind, but you might also mention "$", "&&&", ":" and so on.
Post a reply to this message
|
![](/i/fill.gif) |