Here comes part III of the Hamler blog post series …
One of the observations was that for/on Day21 hamler ~6000 times slower than haskell, where in general we where able to see that it was 5-20 times slower.
Just looked into this a little bit and there is good news and bad news. The good news is I found something.
I can make the hamer solution run 10 times faster by changing the implementation of part1 from …
foodsFreeOfAllergens = go foods false where
go foods' true = foods'
go foods' false = go foods'' (foods' == foods'') where
foods'' = removeIngredientsBySingleton $ removeIngredientsByIntersection foods'
… to …
(foodsFreeOfAllergens, _) = go foods false 0 where
go foods' true c = (foods', c)
go foods' false c = go foods'' (foods' == foods'') (c + 1) where
foods'' = removeIngredientsBySingleton $ removeIngredientsByIntersection foods'
Can you see it? I am passing an extra parameter/counter into the recursion (and ignore it).
My best guess is that the first implementation should be tail recursive, but the compiler fails to pick up that it is and the second implementation forces it to be tail recursive (somehow).
Now Day21 is in the ballpark with the other solutions.
The bad news is: I have no idea what is going on here. And Day11, Day16 and Day21 are still 200-600 times slower than haskell for a reason that I still need to discover/understand. Stay tuned …