Comment by kreyenborgi
3 months ago
Don't think of it as being all pure code, think of it as tracking in the type system which parts of your code may launch the missiles and which parts can't. Given the following program,
main :: IO ()
main = do
coordinates <- getCoords
launch trajectory
where
trajectory = calcTrajectory coordinates
getCoords :: IO Coordinates
getCoords = -- TODO
launch :: Trajectory -> IO ()
launch = -- TODO
calcTrajectory :: Coordinates -> Trajectory
calcTrajectory = -- TODO
I can look at the types and be reasonably certain that calcTrajectory does no reads/writes to disk or the network or anything of that sort (the part after the last arrow isn't `IO something`), the only side effect is perhaps to heat up the CPU a bit.
This also nudges you in the direction of an Functional Core, Imperative Shell architecture https://www.destroyallsoftware.com/screencasts/catalog/funct...
>as tracking in the type system which parts of your code may launch the missiles
given that Haskell is lazy by default there's a million ways to shoot yourself in the foot through memory leaks and performance issues (which is not unlike the problems the IO type attempts to make explicit in that domain), so I never really understand this kind of thing. Purity doesn't say much about safety or semantics of your code. By that logic you might as well introduce a recursion type and now you're tagging everything that is recursive because you can easily kill your program with an unexpected input in a recursive function. To me this is just semantics you have to think through anyway, putting this into the type system just ends up creating more convoluted programs.
FYI, I think you meant functional core, imperative shell.
haha yes, thanks!