Re: [Haskell-cafe] Re: Functional version of this OO snippet
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
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
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
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
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
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
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
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