Re: [Haskell-cafe] Re: Functional version of this OO snippet

2008-12-05 Thread ajb

G'day all.

Thomas Davie wrote:


class IEngine a where
  foo :: a -> String
  bar :: a -> String -> String


"Apfelmus, Heinrich" <[EMAIL PROTECTED]> replied:


You don't even need a type class, a simple data type is enough.

  data Engine = Engine { foo :: IO (), bar :: String -> IO () }


There is a tradeoff here, that you should be aware of.

1. Using typeclasses.

Pro: It works with the SPECIALIZE pragma.

Pro: You can put arbitrary data in the concrete data type which is
the instance of IEngine.  (If you don't, incidentally, this is called
a "traits typeclass".)

Con: You can't generate instances of IEngine dynamically at run-time
(at least without using unportable unsafeCast# magic).  So you're
limited to only those implementations that you (possibly dynamically)
link in.

2. Using records.

Pro: Usually simpler, and using fewer lines of code.

Pro: You can generate new "instances" at will, and you're not limited
to that which you link in.

Con: Usually more explicit arguments passed around.

Con: If your methods involve polymorphism, then the record will turn
out to have a higher-rank type, which isn't valid Haskell 98.


This always works because all object methods expect a "self" argument.


That's not true for OO languages which have virtual constructors.

Haskell typeclasses support virtual constructors/factory methods just
fine, because the "self" type can appear anywhere in the signature,
including being the return value.  The monad "return" method is one
example of this.

Cheers,
Andrew Bromage
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Re: Functional version of this OO snippet

2008-12-05 Thread Jonathan Cast
On Fri, 2008-12-05 at 18:50 +0100, Thomas Davie wrote:
> On 5 Dec 2008, at 17:46, Duncan Coutts wrote:
> 
> > On Fri, 2008-12-05 at 17:06 +0100, Thomas Davie wrote:
> >> On 5 Dec 2008, at 17:00, Duncan Coutts wrote:
> >>
> >>> On Fri, 2008-12-05 at 16:50 +0100, Thomas Davie wrote:
> >>>
>  Sure, and he could then use a fold instead of a map.  Reading files
>  is
>  problematic, but as long as you're only doing it once (the most
>  common
>  situation) is entirely fine wrapped up in an unsafePerformIO.
> >>>
> >>> No!
> >>>
> >>> Please don't go telling people it's entirely fine to use
> >>> unsafePerformIO
> >>> like that (or at all really).
> >>
> >> Exactly what isn't fine about it?
> >
> > It's the antithesis of pure functional programming. It's so unsafe  
> > that
> > we don't even have a semantics for it.
> Yes, but we also don't have semantics for IO, so it's no real surprise  
> that we have none for something that runs an IO action.

We don't have a (denotational) semantics for Haskell, either.  But, in
principle, one could be produced if desired.  The pure subset is easy;
IO only requires a semantics for the underlying OS and --- in the
presence of the FFI --- the C language.

Unless you add unsafePerformIO or evaluate to the language.  I think
that, if someone was sufficiently motivated to work out the details,
that you could prove that there is *no* denotational semantics for IO
which admits either function without changing the definition of one or
more pure Haskell types.

jcc


___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Re: Functional version of this OO snippet

2008-12-05 Thread Thomas Davie


On 5 Dec 2008, at 17:46, Duncan Coutts wrote:


On Fri, 2008-12-05 at 17:06 +0100, Thomas Davie wrote:

On 5 Dec 2008, at 17:00, Duncan Coutts wrote:


On Fri, 2008-12-05 at 16:50 +0100, Thomas Davie wrote:


Sure, and he could then use a fold instead of a map.  Reading files
is
problematic, but as long as you're only doing it once (the most
common
situation) is entirely fine wrapped up in an unsafePerformIO.


No!

Please don't go telling people it's entirely fine to use
unsafePerformIO
like that (or at all really).


Exactly what isn't fine about it?


It's the antithesis of pure functional programming. It's so unsafe  
that

we don't even have a semantics for it.
Yes, but we also don't have semantics for IO, so it's no real surprise  
that we have none for something that runs an IO action.



One needs pretty special justification for using unsafePerformIO and
such cases should be hidden in libraries presenting pure interfaces,  
not

used willy-nilly in general application code.

Note that I'm not claiming that it's necessarily going to do bad  
things
in the specific case you're imagining using it in. However just  
because
it happens not to do bad things in this case does not mean that it's  
ok

to use it here or in general.


No, and I never said that it should be used more generally -- I was  
very careful that in this case I was following the rules for making  
sure that verifyItsSafeYourselfPerformIO was indeed safe.


Bob

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Re: Functional version of this OO snippet

2008-12-05 Thread Duncan Coutts
On Fri, 2008-12-05 at 17:06 +0100, Thomas Davie wrote:
> On 5 Dec 2008, at 17:00, Duncan Coutts wrote:
> 
> > On Fri, 2008-12-05 at 16:50 +0100, Thomas Davie wrote:
> >
> >> Sure, and he could then use a fold instead of a map.  Reading files  
> >> is
> >> problematic, but as long as you're only doing it once (the most  
> >> common
> >> situation) is entirely fine wrapped up in an unsafePerformIO.
> >
> > No!
> >
> > Please don't go telling people it's entirely fine to use  
> > unsafePerformIO
> > like that (or at all really).
> 
> Exactly what isn't fine about it?

It's the antithesis of pure functional programming. It's so unsafe that
we don't even have a semantics for it.

One needs pretty special justification for using unsafePerformIO and
such cases should be hidden in libraries presenting pure interfaces, not
used willy-nilly in general application code.

Note that I'm not claiming that it's necessarily going to do bad things
in the specific case you're imagining using it in. However just because
it happens not to do bad things in this case does not mean that it's ok
to use it here or in general.

Duncan

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Re: Functional version of this OO snippet

2008-12-05 Thread Thomas Davie


On 5 Dec 2008, at 17:00, Duncan Coutts wrote:


On Fri, 2008-12-05 at 16:50 +0100, Thomas Davie wrote:

Sure, and he could then use a fold instead of a map.  Reading files  
is
problematic, but as long as you're only doing it once (the most  
common

situation) is entirely fine wrapped up in an unsafePerformIO.


No!

Please don't go telling people it's entirely fine to use  
unsafePerformIO

like that (or at all really).


Exactly what isn't fine about it?

Bob
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Re: Functional version of this OO snippet

2008-12-05 Thread Duncan Coutts
On Fri, 2008-12-05 at 16:50 +0100, Thomas Davie wrote:

> Sure, and he could then use a fold instead of a map.  Reading files is  
> problematic, but as long as you're only doing it once (the most common  
> situation) is entirely fine wrapped up in an unsafePerformIO.

No!

Please don't go telling people it's entirely fine to use unsafePerformIO
like that (or at all really).

Duncan

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Re: Functional version of this OO snippet

2008-12-05 Thread Thomas Davie


On 5 Dec 2008, at 16:42, Apfelmus, Heinrich wrote:


Thomas Davie wrote:

You don't even need a type class, a simple data type is enough.


Very true, but I disagree that you've made it functional in any  
way, IO

is all about sequencing things, it's very much not a functional style


data Engine = Engine { foo :: IO (), bar :: String -> IO () }


This is much nicer done as functions from String -> String


Sure, I agree. I was just replicating  foo  and  bar  from the OP
because I don't know what kind of effect he had in mind. I mean,  
instead
of merely mapping each command in isolation, he could want to  
accumulate

a value or read files or something.


Sure, and he could then use a fold instead of a map.  Reading files is  
problematic, but as long as you're only doing it once (the most common  
situation) is entirely fine wrapped up in an unsafePerformIO.


Either way, the question was how to do it functionally, and to do it  
functionally, and with all of the nice shiny benefits we get with  
functional code like composibility and orthogonality, you need to do  
it with String -> String.


Bob

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Re: Functional version of this OO snippet

2008-12-05 Thread Thomas Davie

You don't even need a type class, a simple data type is enough.


Very true, but I disagree that you've made it functional in any way,  
IO is all about sequencing things, it's very much not a functional style


 data Engine = Engine { foo :: IO (), bar :: String -> IO () }

 run e = processCommand e =<< getLine

 processCommand e c
   | "foo" `isPrefixOf` c = foo e   >> run e
   | "bar" `isPrefixOf` c = bar e c >> run e
   | otherwise= return ()


This is much nicer done as functions from String -> String, it becomes  
much more compassable, removes a sequential style from your code and  
stops processCommand depending on always working with the "run"  
function making it a bit more orthogonal.


data Engine = Engine {foo :: String, bar :: String -> String}

run e = unlines . map (proccesCommand e) . lines

processCommand e c
  | "foo" `isPrefixOf` c = foo e
  | "bar" `isPrefixOf` c = bar e c
  | otherwise   = ""

Bob
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe