learning to use Haskell types

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

learning to use Haskell types

Michael Mossey
This is really a general question, but any advice is welcome.

I'm currently working through Typeclassopedia and all the tutorials it
links to. I'm really enjoying it, but a kind of scary question is: when I
begin to write "real" software in Haskell, how will I find natural places
to use the types and how will I make the transition to idiomatic Haskell?

[Note: the main application I have for Haskell is my music composition
hobby. I plan to use Haskell to write software for editing music and for
computer-assisted composition (CAC). CAC means the computer does some of
the tedious work involved in searching for, and evaluation of, combinations
of musical themes. All my algorithms are personal to my own process. There
is no off-the-shelf software that can help me. That's why it's fortunate
I'm a programmer. The CAC features need to be tightly integrated into a
musical score editor, hence I have to write that too. Really quite a fun
project, and even if it takes two years, I will get years and years of
enjoyment from it!]

Then it occurred to me that a common theme in Haskell is to provide a way
to express a solution to a problem in an expressive form that is natural to
that problem. So maybe what I need to do is stop thinking, "Where can I use
this type," and instead dream up ways to express ideas in a simple and
natural form, then make use of Haskell types and type classes to make that
expressive form a reality.

For example, in a music editor, there are many actions that create new
notes. A note needs many pieces of information to describe it: the note's
place in time, its duration, dynamics, whether tied to successive notes,
type of flag or beam... Much of this information can be inferred from the
context in which the note is created, and so a natural expressive language
would bring a new note into existence with a minimal need for providing
details. Those details would be inferred from the context. So that's a
Reader monad right there.

Thanks,
Mike



Reply | Threaded
Open this post in threaded view
|

Re: learning to use Haskell types

Heinrich Apfelmus
Michael Mossey wrote:

> This is really a general question, but any advice is welcome.
>
> I'm currently working through Typeclassopedia and all the tutorials it
> links to. I'm really enjoying it, but a kind of scary question is: when
> I begin to write "real" software in Haskell, how will I find natural
> places to use the types and how will I make the transition to idiomatic
> Haskell?
>
> [...]
>
> Then it occurred to me that a common theme in Haskell is to provide a
> way to express a solution to a problem in an expressive form that is
> natural to that problem. So maybe what I need to do is stop thinking,
> "Where can I use this type," and instead dream up ways to express ideas
> in a simple and natural form, then make use of Haskell types and type
> classes to make that expressive form a reality.

Yes, that's the best approach. The hard part is finding a formulation
that is natural to the problem; and by definition it's hard to give
general advice there. I know of no other way than learning from
examples, like this one

    Simon Peyton Jones, Jean-Marc Eber, Julian Seward.
    "Composing contracts: an adventure in financial engineering"
    http://research.microsoft.decenturl.com/composing-contracts

What the Typeclassopedia can help with is to provide a few known
concepts where it's always worth checking whether they apply naturally.
I'd recommend the following order

    Monoid
    |
    |
    Applicative
    Monad
    |
    Arrow

In particular, Monoids are very common. The others usually only apply
when the problem domain involves polymorphism, i.e. when the object of
discourse is a type constructor.


That being said, the many concrete applicative functors and monads like
state monads, the list monad, zip lists, parser combinators,
probabilistic monads etc. are very useful implementation techniques. But
they are limited to "small scale" simplifications, they generally do
*not* help with finding a formulation that is natural to the whole
problem domain. (Except when it's very obvious, i.e. when the problem
*is* to model a state machine, to parse something, to sample a
probability distribution, etc.)


> For example, in a music editor, there are many actions that create new
> notes. A note needs many pieces of information to describe it: the
> note's place in time, its duration, dynamics, whether tied to successive
> notes, type of flag or beam... Much of this information can be inferred
> from the context in which the note is created, and so a natural
> expressive language would bring a new note into existence with a minimal
> need for providing details. Those details would be inferred from the
> context. So that's a Reader monad right there.

This would be an example where I think that the Reader monad is an
implementation detail, not a model of the problem domain. (Not to
mention that I think that the Reader monad has very limited
applications.) The latter would be about trying to eliminate the need
for a context altogether, to group the many details so that they can be
"polymorphized away", etc.


Regards,
apfelmus

--
http://apfelmus.nfshost.com

Reply | Threaded
Open this post in threaded view
|

Re: learning to use Haskell types

Heinrich Apfelmus
Michael Mossey wrote:

> Heinrich Apfelmus wrote:
>> This would be an example where I think that the Reader monad is an
>> implementation detail, not a model of the problem domain. (Not to
>> mention that I think that the Reader monad has very limited
>> applications.) The latter would be about trying to eliminate the need
>> for a context altogether, to group the many details so that they  
>> can be
>> "polymorphized away", etc.
>
> Can you explain more about what it means to "polymorphize away"  
> details? I'm not clear about that.
>
> You mention "grouping" details. Does this mean creating data which  
> types that hold all the details, and the data type itself is some  
> kind of larger concept? For example, you could say a note has many  
> details such as time, duration, dynamic, etc. And you could also  
> create
>
> data Note = Note { time :: Ratio, duration :: Ratio, dyn :: Dynamic }
>
> and work with Notes as much as possible without peering inside them.


Yes, the goal is to avoid peering inside. Creating a new abstraction  
(= no peeking inside) is the only way to make things elegant. Using  
record data types are a first step towards that goal.

Another technique is to use parametric polymorphism, that's what I  
intend to convey with the phrase "polymorphize away". The idea is  
that the type system can make sure that you don't peek inside.  
Consider the following (almost too trivial) example:

     length :: [Note] -> Int

This function is intended to calculate the length of a list of notes.  
But of course, it's wholly unimportant that the lists consists of  
notes, it could as well be a list of apples or integers. In fact, the  
specialization to notes is a mental burden, and it's much simpler to  
write a length function that does not care about the contents

     length :: [a] -> Int

The key point is that the type alone already ensures that  length  
*cannot* peek inside the list elements, because it's polymorphic in  a .

Another example is the function

     sortBy :: (a -> a -> Ordering) -> [a] -> [a]

which does not want to know anything about the list elements except  
that they can be compared.

Of course, these were rather general and well-known examples; the key  
is to find similar patterns in your own code and problem domain. For  
instance, notes in staff notation don't really care about velocities,  
notes in a piano roll are actually just rectangles in a grid, etc.  
The goal is to focus solely on the relevant details and hide the  
unimportant details behind a type variable  a .


Regards,
apfelmus

--
http://apfelmus.nfshost.com