5 ms·
Well, it clearly isn't. It's an improvement, but "<=<" is still quite different from ".". I can see it from here. I'm sure there's an entirely justifiable reas
by prof_hobart 13y ago
Well, it clearly isn't. It's an improvement, but "<=<" is still quite different from ".". I can see it from here.
I'm sure there's an entirely justifiable reason why the two syntaxes have to be different, but that doesn't stop it being a bit of a pain when it comes to trying to learn the language - I've developed a bunch of side effect-free code, and I want to stick a bit of logging in the middle of one bit to help me see exactly what's going on, and suddenly I seem to need to rewrite half my code to use a very similar, but subtly different syntax.
- tome 13y agoGranted, it's not exactly the same. > I seem to need to rewrite half my code to use a very similar, but subtly different syntax. Half is a massive over exaggeration if you've written it in the right way, i.e. the in the composable manner, preferring (.) and (<=<) over function application and (>>=). However, this I don't know how widely appreciated this style is. It's certainly not taught first in functional programming, unfortunately.
- prof_hobart 13y agoI certainly don't think it's the style I've picked up from any of the books/tutorials I've seen. But even changing several levels of call from "." to "<=<" (and presumably back again when I've finished) because I've added a temporary logging call somewhere in the stack, still seems rather a pain.
- polymatter 13y agoI am not an expert by any stretches of the imagination, but I have some experience of Haskell at least. Currently writing a web app with Yesod (go check it out!). Outside of the trivial pure functions, pretty much all real work is done inside or one monad or another. At which point there is no change in syntax or level at all. If you just want a temporary log consider using Haskell's debug features: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Debug-Trace.html http://www.haskell.org/ghc/docs/latest/html/libraries/base/D... "For example, this returns the value of f x but first outputs the message. trace ("calling f with x = " ++ show x) (f x)" A good analogy is moving from procedural to object oriented code. It seems like a massive transition to organise code into separate objects which are instantiated rather than just using function pointers or gotos where you need to. But objects allow you to talk about frameworks and different architectures, separation of responsibilities and in general manipulate your code at a much higher level. Its worth pushing through to understand objects even if initially they are harder than naive procedural code. Similarly its worth taking the investment to understand monads rather than try to avoid them. In the longer run, they provide a higher level structure to the code.
- dllthomas 13y agoWhat about: import Prelude hiding ((.)) import Control.Category import Control.Arrow half' = Kleisli half ... runKleisli (half' . half') 8
- tome 13y agoVery nice.
- prof_hobart 13y agoInteresting. Does point to another reason why I've struggled with Haskell so far though - not having done university-level maths, I regularly hit terms (such as Kleisli categories) that mean absolutely nothing to me, and looking it up on Wikipedia makes my brain bleed. I appreciate this is my problem rather than the language's, but I've worked in a huge array of different languages in the past 30 years and it's only ever functional ones where I hit the "what the hell does this even mean?" wall whilst trying to learn them...
- tome 13y agoTrying to understand monads and the Kleisli newtype by looking at the Wikipedia pages on monads and Kleisli categories is probably going to do more harm than good. Trying to understand what a monad "is" is probably not worthwhile. It's actually quite easy to work out how to use them just by playing around with them.
- dllthomas 13y agoYeah, I don't think I've actually used Kleisli in production code. Learning what a monad "is" is very worthwhile, but it's not where you start, because it's only tangentially related to how a monad is used.
- dustingetz 13y agoyou can also rewrite `half . half $ 8` as `half <=< half $ 8` in the identity monad. So if you aren't sure what monad you need, you can write the code against the identity monad and then change the monad later when you need to add logging. An example where this shines is writing interpreters. You can write a "calculator" interpreter in identity monad, then add an environment (functions and lexical scope), then add errors, then make the environment mutable, then add continuations and try/catch. The core eval loop does not change as you implement new keywords like 'def' or 'throw'. In an imperative interpreter, each feature is coupled with the interpreter core. I've done this in python but writing pure code in python is so crappy that it's not even worth sharing. Here is a short blog post though with code samples: http://www.dustingetz.com/2012/10/02/reader-writer-state-monad-in-python.html http://www.dustingetz.com/2012/10/02/reader-writer-state-mon...
- acomar 13y ago(<=<) is a generalization of (.), and it's one of a number of historical issues that many Haskellers would like to see resolved, but everyone is afraid to try because it would basically break every single available library (many of which are no longer actively maintained). This is basically a library issue, not a syntax issue. A similar issue is that the Monad typeclass doesn't require a Functor instance, even though it's mathematically necessary. There's a long standing argument over how the Prelude (standard library) should be updated/modified, but there's a lot of bikeshedding going on.
- prof_hobart 13y agoAs someone learning the language, I'm not sure I care whether it's a syntax issue or a library issue. What I see is a language issue, and one that gets in the way of making learning it straightforward.
- tome 13y agoI don't think this is correct. My understanding is that it's not a library issue, and in terms of Haskell types (<=<) is not a generalisation of (.) (although it is mathematically speaking). If you want a -> m b to be a Category instance so you can use (.) you need the Kleisli newtype wrapper. If you want a -> b to work in any monad so you can use (<=<) you need to compose with return to get Monad m => a -> m b. Either way you have to explicitly specify the isomorphism explicitly and I don't think there's any getting around that.