Jesus Christ, fuck fuck monads and fuck haskell. This language is awful.
Edit: I don't want to keep making rage-posts, so I'm just going to edit this one every time I encounter something new.
This snippet works:
import Numeric.LinearAlgebra
main = do
let m = (3><3) ([1..] :: [Double])
let d = 0.5 :: Double
let m' = 0.1 * m
putStrLn (show m')
...but, this one won't compile:
import Numeric.LinearAlgebra
main = do
let m = (3><3) ([1..] :: [Double])
let d = 0.5 :: Double
let m' = (0.1 / d) * m
putStrLn (show m')
I repeat, fuck this god-awful, useless language.
like 5 minutes later, I've narrowed it down a little:
let m' = (0.5 ) * (m :: Matrix Double) -- works
let m' = (0.5 :: Double) * (m :: Matrix Double) -- doesn't
Why on earth that makes any difference, I have no clue.
I found out why it's that way, and it's incredibly stupid. This is what ultimately works:
let m' = cmap (*d) m
Edit 2: Space Leaks
A bunch of simple recurrent functions resulted in a joyous occasion in the process of learning Haskell: my very first "space leak"! A "space leak" is the cutesy name for 'a tail-recursive function that doesn't get optimized into a loop, and keeps producing thunks for intermediate values until your system runs out of memory and dies'. It's not
exactly a memory leak, because the system hasn't lost the handle to that memory: in principle, if the computation finished, it would clean up all that memory and correctly terminate. It just winds up at the same place: allocating an obscene amount of memory, possibly hanging your system.
The problem comes in part from one of the glorious selling-points of Haskell, it's lazyness; it turns out that that lazyness often sabotages building tail-recursive loop optimization. The comical solution, which it's taken me more than a week to figure out, is: "use ! to mark every god damned thing in the function as strict", i.e. "not-lazy". Doing that has changed my memory requirements from "reaching 12G and then hanging the system" to
50 Kb. I guess I get to look forward to a glorious future of marking every production with ! in a desperate attempt to ask GHC to kindly not piss away all my system's memory.
Edit 3:
Speaking of: run time for a GRU: 55 minutes in Python, 129 minutes in Haskell. I don't know why.
Edit 4:
Cabal. On the down-side, Cabal took a lot of fighting to get working, and I kind of feel like I shouldn't have needed to set up Cabal and a local package repository just to enable profiling. On the up-side, though, now that it's actually working, it is kind of cool, I have to admit. `cabal repl` made getting a local-packages-aware interpreter pretty easy, I'm pretty grateful for that.