Hello there, surfer of the interwebs! This is a log of my progress learning the programming language Haskell by solving the theoretical programming challenges found on Project Euler. Iām still working on making my code more legible and efficient, the latter of which is not Haskellās greatest strength, though itās astoundingly efficient for its level of abstraction (praise be to Simon Peyton Jones & Jan-Willem Maessen et al.).
Have fun reading! š
- Get comfortable with standard library. Pretty much the first step when learning any language is getting used to the nitty-gritty; learning the principles and idioms follows.
- Get used to HindleyāMilner type system, especially the type class hierarchy (which is nearly as contrived as the class hierarchy in OO languages, IMO, though I might change my mind once I get used to it).
- Try to avoid the accumulator pattern, at least where I donāt specifically need it for tail recursion. Having two versions of every function makes code much less readable, and I donāt really feel like using a
where
clause to inline a multi-line function definition.
- Move utility functions to external modules. Iāve begun reusing certain functions (such as
divBy
andprime
) throughout Project Euler, so this would reduce duplications and make code more readable (if spread out). - Remove commented sections. I started work on some pretty cool functions before realizing I didnāt actually need them for the task at hand. However, Iām sure theyād be useful for some use cases, and I couldnāt find anything similar on Hoogle. These will remain available in the commit log.
- Try out Vim/Emacs. Iāve been using Sublime Text with the SublimeHaskell and Hoogle plugins, but I could probably benefit from a tighter and more consistent set of keyboard shortcuts, a simpler API, and the comfort of knowing Iām supporting open source. Might try Evil Mode, Spacemacs, or some other bastard child of the two major CLI editors.
- Efficient? Runs in about .02 seconds, according to the Unix
time
utility. Thatās pretty efficient as far as Iām concerned. (And yes, I watched out for running the same program a few times in a row. Haskell is surprisingly well-optimized by the CPU cache, from what I can tell.) - Simple? Just two lines of definitions, taking advantage of list comprehensions and
any :: (a -> Bool) -> [a] -> Bool
. I probably couldnāt get better than this without pulling out the only function definition in the entire program. - Learned? Not all that much. Mainly got used to the type systemāspecifically, the nature of
mod :: Integral a => a -> a -> a
.
- Efficient? .03 seconds.
- Simple? Pretty simple at this point, though I wish there were some way to collapse the two function definitions required to make use of an accumulator parameter into one more compact definition.
- Notes: Started out as somewhat of a cheat, where I found out how many even Fibonacci numbers were less than four million and hard-coded that value into the program by feeding it to
take :: Int -> [a] -> [a]
. This was becausefilter :: (a -> Bool) -> [a] -> [a]
has no facilities to avoid enumerating the entire list, even when the given function is(< 4000000)
, since the type system canāt prove that the sequence is ever-increasing and therefore can never assume that the rest of the infinite list need not be checked. - Learned? I made
stop :: (a -> Bool) -> [a] -> [a]
in response; it works recursively to move items over one by one until an item matches the stop-test. Fairly proud of this solution, though I wonder whether I couldāve used a function that was already somewhere in the standard library. On another note, this was the only problem for which I consulted the Internet, but this taught me aboutzipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
as it can be used to definefibs
. Not sure whether I could rewrite that definition off the top of my head; I should probably get used to defining lists in terms of initial items and how the following terms relate to them, as I donāt want to move past this without ensuring that I know what I did.