Can anyone explain the following behavior (GHCi 6.6):
Prelude Control.Monad.ST> runST (return 42) 42 Prelude Control.Monad.ST> (runST . return) 42 <interactive>:1:9: Couldn't match expected type `forall s. ST s a' against inferred type `m a1' In the second argument of `(.)', namely `return' In the expression: (runST . return) 42 In the definition of `it': it = (runST . return) 42 Thanks, Yitz _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
Yitzchak Gale wrote:
> Can anyone explain the following behavior (GHCi 6.6): > > Prelude Control.Monad.ST> runST (return 42) > 42 > Prelude Control.Monad.ST> (runST . return) 42 > > <interactive>:1:9: > Couldn't match expected type `forall s. ST s a' > against inferred type `m a1' > In the second argument of `(.)', namely `return' > In the expression: (runST . return) 42 > In the definition of `it': it = (runST . return) 42 Section 7.4.8 of GHC manual states that a type variable can't be instantiated with a forall type, though it doesn't give any explanation why. Hazarding a guess, I suggest it *might* be due to the fact that forall s. ST s a means forall s. (ST s a) whereas you'd need it to mean (forall s. ST s) a in order for it to unify with (m a). Just a guess - I'd be interested to know the real reason as well. Brian. -- http://www.metamilk.com _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Yitzchak Gale
On Jan 1, 2007, at 6:02 , Yitzchak Gale wrote: > Prelude Control.Monad.ST> (runST . return) 42 > > <interactive>:1:9: > Couldn't match expected type `forall s. ST s a' > against inferred type `m a1' I think the problem is that technically runST is a data constructor (possibly not relevant) which takes a function as a parameter (definitely relevant). In the normal compositional model, (f . g) x = f (g x), you're conceptually invoking f on the result of g x (g is independent of f); here, you're lifting the function g x into the ST s a monad via f (g is dependent on f). -- brandon s. allbery [linux,solaris,freebsd,perl] [hidden email] system administrator [openafs,heimdal,too many hats] [hidden email] electrical and computer engineering, carnegie mellon university KF8NH _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Brian Hulley
I wrote:
>> Prelude Control.Monad.ST> runST (return 42) >> 42 >> Prelude Control.Monad.ST> (runST . return) 42 >> >> <interactive>:1:9: >> Couldn't match expected type `forall s. ST s a' >> against inferred type `m a1' Brian Hulley wrote: > Hazarding a guess, I suggest it *might* be due to the fact that > > forall s. ST s a > > means > > forall s. (ST s a) > > whereas you'd need it to mean > > (forall s. ST s) a > > in order for it to unify with (m a). But then why does "return 42" type successfully as forall s. (ST s a)? It needs that same unification. -Yitz _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Brandon S Allbery KF8NH
Brandon S. Allbery KF8NH wrote:
> I think the problem is that technically runST is a data constructor > (possibly not relevant) No, at least not in GHC. It is a function. > which takes a function as a parameter (definitely relevant). It takes the type (forall s. ST s a) as its only parameter. How is that more or less a function than anything else? > In the normal compositional model, (f . g) x > = f (g x), you're conceptually invoking f on the result of g x (g is > independent of f); here, you're lifting the function g x into the ST > s a monad via f (g is dependent on f). I don't think I am using any special lifting mechanism here. The "return" function does exploit the fact that ST s has a Monad instance, but I only used the "return" function for simplicity. The same thing happens if you construct a function that explicitly returns (forall s. ST s a) and use that instead of "return": Prelude Control.Monad.ST> :set -fglasgow-exts Prelude Control.Monad.ST> let {f :: a -> (forall s. ST s a); f x = return x} Prelude Control.Monad.ST> runST (f 42) 42 Prelude Control.Monad.ST> (runST . f) 42 <interactive>:1:9: Couldn't match expected type `forall s. ST s a' against inferred type `ST s a1' ... Here is another possible clue to what is happening. When I try to define that same function f using monomorphic syntax, it fails: Prelude Control.Monad.ST> let {f :: a -> (forall s. ST s a); f = return} <interactive>:1:39: Inferred type is less polymorphic than expected Quantified type variable `s' escapes Expected type: a -> forall s1. ST s1 a Inferred type: a -> ST s a In the expression: return In the definition of `f': f = return (Of course, the MR is not relevant here, because I am providing an explicit type signature.) _ _ ... ... _ _ -Yitz _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Yitzchak Gale
The plot thickens...
It seems that I can't even use STRefs. Something is really wrong here. Prelude Control.Monad.ST Data.STRef> runST $ do {r<-newSTRef 2; readSTRef r} <interactive>:1:8: Couldn't match expected type `forall s. ST s a' against inferred type `a1 b' In the second argument of `($)', namely `do r <- newSTRef 2 readSTRef r' In the expression: runST $ (do r <- newSTRef 2 readSTRef r) In the definition of `it': it = runST $ (do r <- newSTRef 2 readSTRef r) _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
On 01/01/07, Yitzchak Gale <[hidden email]> wrote:
> It seems that I can't even use STRefs. > Something is really wrong here. > > Prelude Control.Monad.ST Data.STRef> runST $ do {r<-newSTRef 2; readSTRef r} Again, this is due to section 7.4.8 [1] of the GHC user manual, which states that you can't instantiate a type variable with a type involving a forall. You're trying to unify the first parameter of ($) :: a -> b -> a with (forall s. ST s a -> a), which is illegal. Using parentheses works: runST (do r <- newSTRef 2; readSTRef r) However, I'm as much in the dark as you are as to _why_ this is illegal. [1]: http://haskell.org/ghc/docs/latest/html/users_guide/type-extensions.html#universal-quantification -- -David House, [hidden email] _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Yitzchak Gale
Hi
Yitzchak Gale wrote: > Can anyone explain the following behavior (GHCi 6.6): I don't know if I can explain it entirely, or justify it properly, but I do have some idea what the issue is. Type inference with higher-ran types is weird. The primary reference is Practical type inference for arbitrary-rank types Simon Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, and Mark Shields. To appear in the Journal of Functional Programming. http://research.microsoft.com/~simonpj/papers/higher-rank/index.htm 'The paper is long, but is strongly tutorial in style.' Back in the Hindley-Milner days, we knew exactly what polymorphism could happen where. All the free type variables were existential (hence specialisable); generalisation over free type variables happened at let. Unification was untroubled by issues of scope or skolem constants. The machine could always guess what your idea was because you weren't allowed to have interesting ideas. Now you can ask for polymorphism in funny places by writing non-H-M types explicitly. As in runST :: (forall s. ST s a) -> a When you apply runST, you create a non-let source of (compulsory) polymorphism. You get a new type variable a and a new type constant s, and the argument is checked against (ST s a). Let's look. > Prelude Control.Monad.ST> runST (return 42) > 42 Can (return 42) have type ST s a, for all s and some a? Yes! Instantiate return's monad to (ST s) and a to the type of 42 (some Num thing? an Int default?). In made up System F, labelling specialisable unknowns with ? runST@?a (/\s. return@(ST s)@?a (42@?a)) such that Num ?a Now what's happening here? > Prelude Control.Monad.ST> (runST . return) 42 We're trying to type an application of (.) (.) :: (y -> z) -> (x -> y) -> (x -> z) We get two candidates for y, namely what runST wants (forall s. ST s a) and what return delivers (m b) and these must unify if the functions are to compose. Oops, they don't. The point, I guess, is that application in Haskell source code is no longer always translated exactly to application in System F. We don't just get (runST@?a) (return@?m@?b) such that (forall s. ST s ?a) = ?m ?b we get that extra /\s. inserted for us, thanks to the explicit request for it in the type of runST. The type of (.) makes no such request. Same goes for type of ($), so runST behaves differently from (runST $). It's a murky world. Happy New Year Conor _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
Conor and others are right; it's all to do with type inference. There is nothing wrong with the program you are writing, but it's hard to design a type inference algorithm that can figure out what you are doing.
The culprit is that you want to instantiate a polymorphic function (here (.) or ($) in your examples) with a higer-rank polymorphic type (the type of runST, in this case). That requires impredicative polymorphism and while GHC now allows that, it only allows it when it's pretty obvious what is going on --- and sadly this case is not obvious enough. The system GHC uses is described in our paper http://research.microsoft.com/~simonpj/papers/boxy Simon | -----Original Message----- | From: [hidden email] [mailto:[hidden email]] On Behalf Of | Conor McBride | Sent: 01 January 2007 13:49 | To: [hidden email] | Subject: Re: [Haskell-cafe] Composing functions with runST | | Hi | | Yitzchak Gale wrote: | > Can anyone explain the following behavior (GHCi 6.6): | | I don't know if I can explain it entirely, or justify it properly, but I | do have some idea what the issue is. Type inference with higher-ran | types is weird. The primary reference is | | Practical type inference for arbitrary-rank types | Simon Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, and Mark | Shields. | To appear in the Journal of Functional Programming. | | http://research.microsoft.com/~simonpj/papers/higher-rank/index.htm | | 'The paper is long, but is strongly tutorial in style.' | | Back in the Hindley-Milner days, we knew exactly what polymorphism could | happen where. All the free type variables were existential (hence | specialisable); generalisation over free type variables happened at let. | Unification was untroubled by issues of scope or skolem constants. The | machine could always guess what your idea was because you weren't | allowed to have interesting ideas. | | Now you can ask for polymorphism in funny places by writing non-H-M | types explicitly. As in | | runST :: (forall s. ST s a) -> a | | When you apply runST, you create a non-let source of (compulsory) | polymorphism. You get a new type variable a and a new type constant s, | and the argument is checked against (ST s a). Let's look. | | > Prelude Control.Monad.ST> runST (return 42) | > 42 | | Can (return 42) have type ST s a, for all s and some a? Yes! Instantiate | return's monad to (ST s) and a to the type of 42 (some Num thing? an Int | default?). In made up System F, labelling specialisable unknowns with ? | | runST@?a (/\s. return@(ST s)@?a (42@?a)) such that Num ?a | | Now what's happening here? | | > Prelude Control.Monad.ST> (runST . return) 42 | | We're trying to type an application of (.) | | (.) :: (y -> z) -> (x -> y) -> (x -> z) | | We get two candidates for y, namely what runST wants (forall s. ST s a) | and what return delivers (m b) and these must unify if the functions are | to compose. Oops, they don't. | | The point, I guess, is that application in Haskell source code is no | longer always translated exactly to application in System F. We don't | just get | | (runST@?a) (return@?m@?b) such that (forall s. ST s ?a) = ?m ?b | | we get that extra /\s. inserted for us, thanks to the explicit request | for it in the type of runST. The type of (.) makes no such request. Same | goes for type of ($), so runST behaves differently from (runST $). | | It's a murky world. | | Happy New Year | | Conor | | _______________________________________________ | Haskell-Cafe mailing list | [hidden email] | http://www.haskell.org/mailman/listinfo/haskell-cafe _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
Simon Peyton-Jones wrote:
> There is nothing wrong with the program you are writing, > but it's hard to design a type inference algorithm that can > figure out what you are doing. Thank you for your response. What I was actually trying to do was this: It seems to me that a natural notion of a state transformer in the ST monad is the type: STRef s st -> ST s a That is in some sense intermediate between pure monadic state transformers on the one hand, and doing state completely within the ST or IO monad with STRefs or IORefs. The idea is that you could then write converter functions like this: stToState :: MonadState st m => (STRef s st -> ST s a) -> m a That would make it very convenient, for example, to use arrays inside a pure state monad. The type signatures above do ensure (as far as I can see) that the opacity of the ST state thread is not violated. But unfortunately, the protective shield in runST created by the higher-rank polymorphism is too thick. Any ideas? A better approach? Thanks, Yitz _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
On 1/1/07, Yitzchak Gale <[hidden email]> wrote:
> > stToState :: MonadState st m => (STRef s st -> ST s a) -> m a > > That would make it very convenient, for example, to > use arrays inside a pure state monad. > > The type signatures above do ensure (as far as I can see) > that the opacity of the ST state thread is not violated. But > unfortunately, the protective shield in runST created by the > higher-rank polymorphism is too thick. Probably, stToState :: MonadState st m => (forall s. STRef s st -> ST s a) -> m a E.g. stToState :: MonadState st m => (forall s. STRef s st -> ST s a) -> m a stToState f = do s <- get let (x, s') = runST (do r <- newSTRef s x <- f r s' <- readSTRef r return (x, s')) put s' return x -- Best regerds, Tolik _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Yitzchak Gale
Yitzchak Gale wrote:
> It seems to me that a natural notion of a state transformer > in the ST monad is the type: > > STRef s st -> ST s a Are there any useful functions of this type? I guess, your intention is that this "transformer" makes no other use of the ST monad than reading or writing a single variable. It seems, every such function better had a purely functional interface anyway, even if it makes use of runST internally. > stToState :: MonadState st m => (STRef s st -> ST s a) -> m a > > The type signatures above do ensure (as far as I can see) > that the opacity of the ST state thread is not violated. I doubt that. The "transformer" you pass in could have captured references from a different state thread, which is exactly the problem the rank-2 type should prevent. I guess, the type signature you want is stToState :: MonadState st m => (forall s . STRef s st -> ST s a) -> m a which should actually work with runST and which would also be a bit pointless (see above). At least if I got rank-2 types correctly, which isn't guaranteed. > Any ideas? A better approach? Uhm... use MonadState in the first place? The converse is comparatively easily accomplished: stateToST :: STRef s st -> State st a -> ST s a stateToST ref action = do (a, st') <- readSTRef ref >>= runState action writeSTRef ref st' return a -Udo -- "Human legalese is the schema language of our society." -- Tim Berners-Lee in http://w3.org/DesignIssues/Evolution _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe signature.asc (196 bytes) Download Attachment |
I wrote:
>> It seems to me that a natural notion of a state transformer >> in the ST monad is the type: >> STRef s st -> ST s a Udo Stenzel wrote: > Are there any useful functions of this type? Sure. Anything that can be written as a pure state transformer can be written this way, of course. In addition, you can use mutable arrays for speed. Here is a concrete example: Let's say you want to shuffle a large list randomly, within a larger application that lives inside some MTL monad stack. Among other things, your monad m satisfies (RandomGen g, MonadState g m), perhaps after a lift. Well, it turns out that using Data.Sequence or Data.IntMap to shuffle a list becomes prohibitive if you might have more than about 10^5 elements in your list. So in that case you will need to use a mutable array, and you now need ST. Combining ST and MTL can be messy, even in this simple case. You will probably write something with a type like RandomGen g => [a] -> g -> ST s ([a], g) apply runST (tiptoeing carefully around the paradoxes mentioned in this thread), and then build an MTL state thing out of it. Wouldn't it be nice if instead you could just write: shuffle :: (RandomGen g, MonadState g m) => [a] -> m [a] shuffle = stToState . shuffleST > I guess, your intention is that this "transformer" makes > no other use of the ST monad than reading > or writing a single variable. Well, it can do whatever it wants inside, but it needs to expose any state that is shared externally. If that state is complex, you can use the same techniques that you would use for a State monad - either build the complexity into the state type using, say, records, or compose several transformers. In this case composition would be: STRef s st1 -> STRef s st2 -> ST s a > It seems, every such function better have > a purely functional interface anyway, Yes, that is the intended use. > even if it makes use of runST internally. You would not need to, stToState would take care of runST. >> stToState :: MonadState st m => (STRef s st -> ST s a) -> m a >> The type signatures above do ensure (as far as I can see) >> that the opacity of the ST state thread is not violated. > I doubt that. The "transformer" you pass in could have captured > references from a different state thread, which is exactly the problem > the rank-2 type should prevent. Hmm, you're right. > I guess, the type signature you want is > stToState :: MonadState st m => (forall s . STRef s st -> ST s a) -> m a Or use some opaque type or monad and do something unsafe inside. > > Any ideas? A better approach? > Uhm... use MonadState in the first place? You mean use ST in the first place. Yes, but I want to avoid that. > The converse is comparatively > easily accomplished... Yes. Regards, Yitz _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Yitzchak Gale
Hi Thomas,
You wrote: > How do I "import" Control.Monad.ST so I can experiment with it from > ghci and just do > runST > like you had. Instead of qualifying it in some way. In GHCi, use the :module command (:m) for built-in modules, and :load and :add for source files. In Hugs, use :load and :also for both. You need to run Hugs with the -98 flag to use Control.Monad.ST. > how do I do runST as fully qualified? Like this: Prelude> Control.Monad.ST.runST (do {r <- Data.STRef.newSTRef 2007; Data.STRef.readSTRef r}) 2007 Have fun with monads! Regards, Yitz _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Yitzchak Gale
Yitzchak Gale wrote:
> Here is a concrete example: > > Let's say you want to shuffle a large list randomly, > within a larger application that lives inside some > MTL monad stack. Among other things, your monad > m satisfies (RandomGen g, MonadState g m), perhaps > after a lift. > > Well, it turns out that using Data.Sequence or Data.IntMap > to shuffle a list becomes prohibitive if you might have > more than about 10^5 elements in your list. So in that > case you will need to use a mutable array, and you now > need ST. > > Combining ST and MTL can be messy, even in this simple > case. You will probably write something with a type like > > RandomGen g => [a] -> g -> ST s ([a], g) You'd plug a runST in there and get shuffle :: RandomGen g => [a] -> g -> ([a], g) or lift it into a state monad. Telling the world that you messed with imperative code inside is completely pointless, since the only thing you could possibly do with the result anyway is apply runST to it. > Wouldn't it be nice if instead you could just write: > > shuffle :: (RandomGen g, MonadState g m) => [a] -> m [a] > shuffle = stToState . shuffleST It seems, what you really want is shuffleST :: RandomGen g => [a] -> StateT g ST [a] No need to stick the generator into a mutable variable. Maybe you even want a MonadST class, analogous to MonadIO. > >Uhm... use MonadState in the first place? > > You mean use ST in the first place. No, I don't. -Udo _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe signature.asc (196 bytes) Download Attachment |
In reply to this post by Simon Peyton Jones
Simon Peyton-Jones wrote:
> Conor and others are right; it's all to do with type inference. There is nothing wrong with the program you are writing, but it's hard to design a type inference algorithm that can figure out what you are doing. > > The culprit is that you want to instantiate a polymorphic function (here (.) or ($) in your examples) with a higer-rank polymorphic type (the type of runST, in this case). That requires impredicative polymorphism and while GHC now allows that, it only allows it when it's pretty obvious what is going on --- and sadly this case is not obvious enough. I don't know enough type theory to suggest a specific patch, but I hope a future version of GHC can do the right thing for (.) and ($) in this situation (and possibly for other functions that simply rearrange their arguments, like flip). >From a friendliness-to-newbies point of view, these error messages are a tremendous wart. I've been on haskell-cafe for a little over three months and seen postings from three people who were tripped up by this (the first of them was myself). So I can't just tell someone who's just starting to learn Haskell that "f $ g y" is equivalent to "f (g y)"; I have to say "those two are *almost always* equivalent, but if you use $ and the compiler complains about not being able to match the expected and the inferred type and a type signature in the error message has the word 'forall', try rewriting that expression without the $ and see if it compiles". Eeeww. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
On 03/01/07, Seth Gordon <[hidden email]> wrote:
> So I can't just tell someone who's just starting to learn Haskell that > "f $ g y" is equivalent to "f (g y)"; I have to say "those two are > *almost always* equivalent, but if you use $ and the compiler complains > about not being able to match the expected and the inferred type and a > type signature in the error message has the word 'forall', try rewriting > that expression without the $ and see if it compiles". Eeeww. Why would someone just starting to learn Haskell be using ST? The canonical tutorial structure is to start with the pure stuff and only introduce the (more complicated!) impure stuff (ST, IORefs, etc.) in an 'advanced techniques' or similar section. -- -David House, [hidden email] _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
David House wrote:
> >> So I can't just tell someone who's just starting to learn Haskell that >> "f $ g y" is equivalent to "f (g y)"; I have to say "those two are >> *almost always* equivalent, but if you use $ and the compiler complains >> about not being able to match the expected and the inferred type and a >> type signature in the error message has the word 'forall', try rewriting >> that expression without the $ and see if it compiles". Eeeww. > > Why would someone just starting to learn Haskell be using ST? The > canonical tutorial structure is to start with the pure stuff and only > introduce the (more complicated!) impure stuff (ST, IORefs, etc.) in > an 'advanced techniques' or similar section. I (and one other person on this list) ran into this issue when I was trying to use takusen to make Haskell talk to a RDBMS. You obviously need to learn advanced techniques to *implement* such a thing, but you shouldn't need advanced knowledge to *use a library* that happens to use higher-rank polymorphic types in its API. There are many routes to fluency in a language, and not everybody is suitable for the route of "here are the axioms underlying the language and the simplest code that applies them; after you thoroughly understand those, we'll show you how to make something practical". Some of us prefer the route of "here are some examples of how to get practical things done; after you're comfortable with them, we'll show you the theory that underlies them". Actually, I suspect that *most* of us prefer the second route. Set theory is closer to the theoretical foundations of mathematics than arithmetic, but when elementary schools tried teaching kids set theory, it didn't work out so well. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by David House
On 1/3/07, David House <[hidden email]> wrote:
> On 03/01/07, Seth Gordon <[hidden email]> wrote: > > So I can't just tell someone who's just starting to learn Haskell that > > "f $ g y" is equivalent to "f (g y)"; I have to say "those two are > > *almost always* equivalent, but if you use $ and the compiler complains > > about not being able to match the expected and the inferred type and a > > type signature in the error message has the word 'forall', try rewriting > > that expression without the $ and see if it compiles". Eeeww. > > Why would someone just starting to learn Haskell be using ST? The > canonical tutorial structure is to start with the pure stuff and only > introduce the (more complicated!) impure stuff (ST, IORefs, etc.) in > an 'advanced techniques' or similar section. > (slightly OT) It's true that this is the typical way of learning Haskell, but I for one think it's a bad way of learning Haskell. Very few real world programs get by without the "impure" stuff, so if you give the newbie the impression that it isn't there (by postponing it) there's a chance he'll run into a situation where he needs it before it's been even mentioned (queue newbie going "bah, academic language" and switching to C++). I find that the Haskell introductions I like the most are the ones that accompany papers about STM and such. I.e. ones which have to teach the reader about the basics of Haskell IO, but doesn't have enough space to start with the pretty stuff. Mix that with some actual comutations to show off some pretty stuff and you'll have a newbie who is both excited about the cool looking features of the pure aspects of Haskell, but completely aware of the fact that you can do impure stuff as well. Oh yeah, I agree that the relationship between ($) and (.) is a bit ugly from the user's perspective. It may have very good reasons, but I'd prefer consistency towards the user (i.e. spot when someone is using ($) with higher ranked types and do rewriting) rather than consistency towards the compiler. -- Sebastian Sylvan +46(0)736-818655 UIN: 44640862 _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
Hi,
> It's true that this is the typical way of learning Haskell, but I for > one think it's a bad way of learning Haskell. > Very few real world programs get by without the "impure" stuff, so if > you give the newbie the impression that it isn't there (by postponing > it) there's a chance he'll run into a situation where he needs it > before it's been even mentioned (queue newbie going "bah, academic > language" and switching to C++). I agree. It also confuses matters when the newbie is suddenly given a library of IO code to use -- but told to ignore it -- they suddenly start wondering why it is so difficult to do anything "useful" in Haskell. A consequence of which seems that the student becomes afraid (and ignorant) of Haskell. > I find that the Haskell introductions I like the most are the ones > that accompany papers about STM and such. I.e. ones which have to > teach the reader about the basics of Haskell IO, but doesn't have > enough space to start with the pretty stuff. I agree with this also. I don't think it is a difficult feat teaching an absolute beginner how do some some basic stuff with the IO monad. This shows straight away that haskell can do some useful stuff other than adding numbers together in Hugs. >Mix that with some actual > comutations to show off some pretty stuff and you'll have a newbie who > is both excited about the cool looking features of the pure aspects of > Haskell, but completely aware of the fact that you can do impure stuff > as well. Yes, I wish this approach was applied much more often. Semi-related to this: I taught an algorithms and data structures class last term. In one of the classes the students were given an equation to solve and they were frantically typing it into their calculators, replacing the variable parameters with numbers. I told them they could all finish the class in 5 minutes if they used Haskell. Type the equation as it is and use higher-order functions to work out the parameters. The look of horror on the student's faces when I mentioned the 'H' word was priceless. However, they were all prepared to spend 50 minutes writing a Java program which would have the same effect. Chris. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
Free forum by Nabble | Edit this page |