5 ms·
Haskell abstracts things at an even higher level than most programming languages. For example, the infamous monads are a way of abstracting out the details of h
by andolanra 15y ago
Haskell abstracts things at an even higher level than most programming languages. For example, the infamous monads are a way of abstracting out the details of how 'effectful' computations (i.e. computations that do something other than simply taking arguments and returning values) should work. Monads model effects like exceptions, mutable state, input/output, nondeterminism, and so forth, and by abstracting them into the same pattern, it allows you to write functions that work with any effect in the same way. For example, if I have a list of operations which have effects and all have the same result type, I can sequence them together using the sequence function, so for IO:
sequence [putStrLn "foo", putStrLn "bar", putStrLn "baz"]
will print those three strings on their own lines. However, I can use the same function for Haskell's equivalent of null:
sequence [Just 3, Just 8, Just 5] -- results in Just [3, 8, 5]
sequence [Just 3, Nothing, Just 5] -- results in Nothing, because Nothing is
-- propagated if it occurs
or for nondeterminism
sequence [[1, 2], [3, 4], [5, 6]] -- results in every possible list whose first
-- element is drawn from the first list, whose
-- second element is drawn from the second
-- list, and whose third element is drawn from
-- the third list.
or for any other effect that Haskell has. A lot of learning Haskell has to do with looking at the abstractions Haskell offers (monads are only one; you also get things like arrows, functors, applicative functors, &c) and understand how you can phrase your problems in terms of them and consequently use Haskell's abstractly written functions to your favor. A great practical example here is parsing; parser combinators can be expressed as monads or as applicative functors, both of which make your parsing code very small and quite natural to read. For example, the following is a function to parse strings like "(552,864)" using the Parsec parser combinator library:
parseDigit = oneOf ['0'..'9']
parseInt = many parseDigit
parsePairOfInts = do
string "("
p1 <- parseInt
string ","
p2 <- parseInt
string ")"
return (p1, p2)
- ippisl 15y agoParsec looks pretty similar to python's pyparsing or Pysec.Pysec is a "Monadic Combinatoric Parsing" library in python, so i think you could do monads in python.
- Peaker 15y agoIf you can do continuation passing, you can implement any monad instance. You still may not be able to encode the Monad generalization (which indeed, Python cannot). As for continuation passing, Python can do explicit continuation passing, but it has caveats: * No TCO means it doesn't work well in larger cases * Explicit CPS is ugly. yield-based continuation passing is restricted. You can't implement non-determinism via generators. So using yield, you can implement most monad instances in Python (excluding non-determinism). But Haskell can goes beyond a particular/useful monad instance (e.g: Parsing) and has a Monad generalization (basically a type-class that allows lots of libraries to be written such that they work with any monad, not just this parsing monad). Additionally, monad transformers are a technique to compose monad types together to form a new monad that can do the things each of the monads can. For example, I can compose the non-determinism monad with the parsing monad to get a non-determinism parser that yields various possible parse results.
- jeffdavis 15y agoWhy can't you use monads in ruby? A monad is essentially just an interface, right? The original question was about the mechanisms, not the style or libraries. I don't really know haskell, so correct me if I'm wrong.
- Peaker 15y agoType-classes are like interfaces, but not quite the same. They allow return-type polymorphism, which ordinary OO interfaces do not. And the Monad type-class requires this feature. Basically, when you call the "return" function in Haskell, (e.g: return 5) -- the code being called depends on the type of the result. The type of the result is determined from the context (by type-inference or rarely, type annotations). In Ruby or Python, there's no easy/direct way to encode something like "return" such that it works with any Monad instance.
- jeffdavis 15y agoInteresting. I see what you are saying, but could you provide an example (or a link to an example) for clarity? Preferably something not easily accomplished in ruby?
- eru 15y agoI found an article about return-type polymorphism. See http://vpatryshev.blogspot.com/2010/01/dispatch-by-return-type-haskell-vs-java.html http://vpatryshev.blogspot.com/2010/01/dispatch-by-return-ty... One thing is impossible, though: defining two methods that differ only in return type, something like Most important point: Something like int i = parse(String source); long l = parse(String source); boolean b = parse(String source); is possible in Haskell, but not in Python, and barely imaginable in Java.
- Peaker 15y agoElaboration: class Read r where read :: String -> Maybe r Note that it's polymorphic on the type of the return within the Maybe. It's very flexible, the polymorphic type can appear anywhere within the type signature. Now you can write various functions that use "read" and they all remain return-type polymorphic. For example, you can write one that loops, requesting the user to repeat entry until parse-able data (of the wanted type) is given: repeatReadingUntilValid :: Read r => IO r repeatReadingUntilValid = do line <- getLine case read line of Nothing -> do putStrLn $ "Invalid input: " ++ show line repeatReadingUntilValid Just result -> return result That's just a silly example, because read isn't that interesting. Another example is QuickCheck, which uses type-classes to auto-generate fuzz-testers for functions. For example: import Test.QuickCheck pretty :: MyType -> String pretty = .. pretty print my type here .. unpretty :: String -> MyType unpretty = .. parse the pretty printing of my type here .. Now I can test that unpretty is indeed the inverse of pretty: quickCheck (\x -> unpretty (pretty x) == x) (for every x, the unpretty of pretty of x equals x). I can generalize this property to: isInverse f g x = f (g x) == x And then use: quickCheck (isInverse unpretty pretty) Similarly you can define: commutative f x y = x `f` y == y `f` x associative f x y z = (x `f` y) `f` z == x `f` (y `f` z) transitive f x y z = x `f` y && y `f` z ==> x `f` z Which makes an important type of unit testing a breeze. The type system is saving us from writing code here.