Justification fromJust at Haskell

I am trying to learn Haskll, and so I asked question 26 of Project Euler in Haskell: http://projecteuler.net/problem=26

My solution to the problem is this:

answer26 = answer26' 1000 answer26' n = snd $ maximum $ map (\x -> cycleLength x [1]) [2..n - 1] where cycleLength n (r:rs) | i /= Nothing = (1 + fromJust i, n) | r < n = cycleLength n $ (10*r):r:rs | otherwise = cycleLength n $ (r `mod` n):r:rs where i = elemIndex r rs 

I understand that this is not the most efficient algorithm, but, naively O (n ^ 3) (where n = 1000), this is not such a problem. What worries me is that from my understanding of monads, one of their main characteristics is that they, in a sense, “mark” everything that used the monad. The fromJust function seems to fly right in the face of this. Why does he exist? Also, assuming its existence is justified, do I use it in the above code practice?

+4
source share
3 answers

Using partial functions (functions that may not return a value) is usually not recommended. Functions such as head and fromJust exist because they are sometimes convenient; you can sometimes write shorter code, which is more understandable to students. Many functional algorithms are expressed through head and tail , and fromJust conceptually matches head .

It is usually preferable to use pattern matching and avoid partial functions, as this allows the compiler to catch errors for you. In your code fragment, you carefully checked that the value is never Nothing , but in large real code files the code can be many years, 1000 lines are long and are supported by many developers. It is very easy for a developer to override the code and skip such a check. When matching with a pattern, it is located directly in the code structure, and not just in any arbitrary Bool expression.

It's not so difficult to replace using fromJust template:

 answer26 = answer26' 1000 answer26' n = snd $ maximum $ map (\x -> cycleLength x [1]) [2..n - 1] where cycleLength n (r:rs) = case elemIndex r rs of Just i -> (1 + i, n) Nothing -> if r < n then cycleLength n $ (10*r):r:rs else cycleLength n $ (r `mod` n):r:rs 

And (I think) the result is also more clear.

Edit: There seems to be a “theoretically good” place to use fromJust mentioned in Typeclassopedia , although you will need someone other than me to explain wtf what everything is about ..;)

+11
source

The monad interface does not contain any specific function for extracting values ​​from the monad, only for their input ( return ).

However, it does not prohibit such functions. When they exist, they will be specific to each monad (hence the many launch functions: runIdentity , runReader , runWriter , runState ... each with different arguments.)

By design, IO does not have such a “get out” function, and therefore it serves to “capture” unclean values ​​inside the monad. But "inability to get out" is not a prerequisite for monads in general. It is believed that they respect the laws of the monad.

With comonads, things have changed. There is a generic function for extracting values ​​from them ( extract ) that each comonad must execute. But the "put values ​​in" functions, when they exist, change for each specific comonad ( env , store ...)

As for fromJust , it is recommended to avoid it whenever possible, because it is a partial function that may not match at runtime.

+10
source

This template is so widespread that there is even a function for it: maybe :: b → (a → b) → Maybe a → b

In your case, if you execute \ x → (cycleLength x [1], x), that is, build a pair outside the Length loop:

  cycleLength n (r:rs) = maybe (cycleLength n rs') (1+) $ elemIndex r rs where rs' | r < n = (10*r):r:rs | otherwise = (r `mod` n):r:rs 

In addition, since you are only looking for the maximum and not the actual value, it will work even with id instead of (1 +).

+3
source

Source: https://habr.com/ru/post/1497572/


All Articles