6 ms·
I tried implementing some simple imperative algorithms (like union-find) and found the Haskell code very noisy. For example, "var x = f(y)" in an imperative lan
by cousin_it 9y ago
I tried implementing some simple imperative algorithms (like union-find) and found the Haskell code very noisy. For example, "var x = f(y)" in an imperative language might translate to "let x = f y" or "do x <- f y" in a Haskell do block, and the wrong variant won't compile. There's no general purpose idiom you can use to replace all loops, only tons of special purpose HOFs you must memorize. Writing code that mixes different types of side-effecting calls is combinator hell. And so on. Usually an imperative algorithm will be much clearer in an imperative language than in Haskell, and easier to get right on the first try.
- Retra 9y agoJust because it doesn't translate 1-to-1 from another imperative language doesn't mean it isn't imperative itself. Haskell is not in the C family. It's not going to accept straight translations from C, but C doesn't monopolize imperativeness either.
- deleted 9y ago[deleted]
- vmchale 9y ago>There's no general purpose idiom you can use to replace all loops, only tons of special purpose HOFs you must memorize. Ah, but there is! Recursion schemes. >Usually an imperative algorithm will be much clearer in an imperative language than in Haskell, and easier to get right on the first try. Not sure what an "imperative algorithm" is, but I agree there are tasks better suited to imperative languages.
- kqr 9y ago> There's no general purpose idiom you can use to replace all loops, only tons of special purpose HOFs you must memorize. Well, yes, there is: general recursion. You can recreate any loop with plain old recursion. But stating that as a problem is a little like coming to Java and saying that "there's no general purpose idiom you can use to replace goto, only tons of special semantic branching constructs you must memorize." Sure, a valid complaint if you've been forced to only use goto in your life, but not a reflection on good programming practise.
- cousin_it 9y agoI don't think that's true. Say you have an imperative algorithm that modifies an array in a loop. Then the Haskell encoding of that algorithm will use writeArray. But since it's monadic, it won't look like general recursion, it'll need to use a combinator like forM. You could say it's general recursion plus bind and return, but it gets worse. If the loop body needs to use both writeArray and randomness, you need more combinators. I'm not sure anyone can reliably write that stuff on a napkin, the way I can write a loop. That's not like the situation with gotos, because code with gotos is harder to write on a napkin.
- tome 9y agoWhat's wrong with this? import qualified Data.Vector.Mutable as V import Control.Monad (when) import qualified System.Random as R main = do v <- V.replicate 10 0 putStrLn "Initialised to zero" printThemAll v setIncreasing v putStrLn "Set with values increasing by 2" printThemAll v addRandomness v putStrLn "Added a random number to each" printThemAll v printThemAll v = loop 0 (V.length v) where loop i n = do when (i < n) $ do e <- V.read v i print e loop (i + 1) n setIncreasing v = loop 0 (V.length v) where loop i n = do when (i < n) $ do V.write v i (i * 2) loop (i + 1) n addRandomness v = loop 0 (V.length v) where loop i n = do when (i < n) $ do r <- R.randomIO e <- V.read v i V.write v i (e + r) loop (i + 1) n
- cousin_it 9y agoThat's indeed nice, and made me change my mind somewhat. I wonder if you can write a napkin example that mixes ST with randomness, instead of using IO for everything?
- tome 9y agoIt's not too bad to manually thread a seed around. import qualified Data.Vector.Mutable as V import Control.Monad (when) import qualified System.Random as R import Control.Monad.ST st = runST $ do v <- V.replicate 10 0 setIncreasing v addRandomness v printThemAll v = loop 0 (V.length v) where loop i n = do when (i < n) $ do e <- V.read v i print e loop (i + 1) n setIncreasing v = loop 0 (V.length v) where loop i n = do when (i < n) $ do V.write v i (i * 2) loop (i + 1) n addRandomness v = loop initial_g 0 (V.length v) where seed = 1234 initial_g = R.mkStdGen 1234 -- Seed loop g i n = do when (i < n) $ do let (r, next_g) = R.random g e <- V.read v i V.write v i (e + r) loop next_g (i + 1) n