3 ms·
Can you give an example of the full picture to someone who doesnt know haskell? The only functional language I'm familiar with is elixir. I tried learning abou
by Exuma 3y ago
Can you give an example of the full picture to someone who doesnt know haskell? The only functional language I'm familiar with is elixir.
I tried learning about them for fun but couldn't see many examples unless I know all of the weird haskell syntax.
The core basis that I gathered of functors is that they're like anything which is "mappable", the key part being it can be any type, and not something like a list or array. So maybe you have an object data type, and you create a custom function which makes it mappable.
The basis of monads was a functor that returns another functor, and the example I saw was a "stream". Elixir does have streams, and this "makes sense" but I wasn't super satisfied with that answer as I highly doubt all this talk of making it hard to understand is about streams.
Is there an actual example that exists where you can present the different ways to solve it? Ie... you have X data structure, and you want to get Y parts out of it while transforming Z at the same time. Method 1: no monads, Method 2: monads, or something like that?
- assbuttbuttass 3y agoOne example would be to simulate some form of early return from a procedural language. Suppose you have a function which executes some operations, and these operations can sometimes fail, so they return a Maybe (probably Elixer has some form of optional type) You could write this without making use of Monads using: f = case op1 of Nothing -> Nothing Just x -> case op2 x of Nothing -> Nothing Just y -> case op3 y of ... Or you can use the Maybe monad to make this a bit nicer f = op1 >>= (\x -> op2 x >>= (\y -> op3 y >>= (\z -> ...))) Or you would probably use the do notation, which is just shorthand for >>= f = do x <- op1 y <- op2 x z <- op3 y ...
- Tainnor 3y agoI'll reference the binary parsing example I've showed in another comment: https://news.ycombinator.com/item?id=37425640 https://news.ycombinator.com/item?id=37425640 Let's first think about how we would write that second example in an imperative language (loosely modeled after Java but don't yell at me if it doesn't compile): Header parseHeader(ByteInputStream bytes) { int size = bytes.readWord8().toInt(); Word16[] fields = new Array(); for (i : (1...size)) { fields.add(bytes.readWord16le()); } return new Header(fields); } but that's no good in FP, since we're modifying state on a byte input stream, so let's try to make it more functional (let's make some syntax allowances for that, so it will look less like Java): Header parseHeader(ByteArray bytes) { val (size, rest) = bytes.nextWord8(); val (_, allFields) = (1...size).fold((rest, [])) { _, (currentRest, fields) -> val (field, nextRest) = currentRest.nextWord16le(); (nextRest, fields.append(field)) } return Header(allFields); } Now we avoid mutation, so the FP gods are happy, but we have to thread the state through the computation (look at how many variables we used) and it will quickly get out of hand if the structure becomes more complicated. Plus because so many of these variables have the same type, it's easy to accidentally introduce errors. The example with the "Get Header" monad has the advantage of being as simple to read as the imperative example, but purely functional. That works because the Get monad builds up a description of steps to perform, so is purely declarative. It is only executed when I later call it on some actual byte string (e.g. by "runGet parseHeader myBytes"). As an added benefit, this means that I can define the parsing logic without actually calling it. I could also write additional code that takes this representation and does other things on it instead of just running it (e.g. running it with extra logging, or doing some kind of dry-run, etc.)
- Exuma 3y agoOooooo... that makes perfect sense. > That works because the Get monad builds up a description of steps to perform, so is purely declarative. AWESOME way to put it. Thank you...