Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I apply the first partial function that works in Haskell?

Tags:

haskell

Suppose I have a list fns of partial functions from a to b, which I represent as functions from a to Maybe b, and I have an object x of type a. Suppose now that I want to define another function from a to Maybe b which takes the value of the first f x that is not Nothing, if such an f exists in fns, or the value Nothing if no such f exists. So basically it outputs f x for the first f that works, or Nothing if no f works.

It isn't hard to come up with some code that does the job. For example, one can create a list [f x| f <- fns] remove all the Nothings from it, and then take the head of the resulting list, or Nothing if that list is empty. But that feels clumsy, and this seems like the kind of general situation for which there is a much more stylish implementation using some built-in function in Haskell. If so, then I'd be interested to know what it is.

like image 674
user15553 Avatar asked Aug 16 '16 21:08

user15553


People also ask

How do you partially apply a function in Haskell?

If you call a multi-parameter function with less than its total number of parameters, then you are partially applying the function. Here we call sum , our previously defined two-parameter function, with only one argument and receive a new function.

What is a partial function Haskell?

From HaskellWiki. A partial function is a function that is not defined for all possible arguments of the specified type. Examples in the Haskell standard library are: head , tail : undefined for empty lists. (!!) : undefined if the index is at least as big as the list length.

What does () mean in Haskell?

() is very often used as the result of something that has no interesting result. For example, an IO action that is supposed to perform some I/O and terminate without producing a result will typically have type IO () .

What is left and right in Haskell?

The Either type is sometimes used to represent a value which is either correct or an error; by convention, the Left constructor is used to hold an error value and the Right constructor is used to hold a correct value (mnemonic: "right" also means "correct").


1 Answers

Warning: This is a really non-standard solution. But I personally really like it for its elegance - and the underlying mind-bending.

On https://wiki.haskell.org/Pointfree you can find a function named swing. Its implementation and type are confusing at first:

swing :: (((a -> b) -> b) -> c -> d) -> c -> a -> d
swing = flip . (. flip id)

You can get a first idea of what it does when you see its fully applied form:

swing f c a = f ($ a) c

In conjunction with other higher-order functions it does things that almost look like magic. Examples from the link:

swing map       :: [a -> b] -> a -> [b]
swing any       :: [a -> Bool] -> a -> Bool
swing foldr     :: b -> a -> [a -> b -> b] -> b
swing zipWith   :: [a -> b -> c] -> a -> [b] -> [c]
swing find      :: [a -> Bool] -> a -> Maybe (a -> Bool)
swing partition :: [a -> Bool] -> a -> ([a -> Bool], [a -> Bool])

All these functions do exactly what you would assume from the types. (But note that the wiki is a little out of date. By now, most of these functions automatically work for any Foldable type.)

The function that you search could start from

swing mapMaybe :: [a -> Maybe b] -> a -> [b]

and then apply listToMaybe. (Both functions are from Data.Maybe)

A more general form would be

swing mapM :: (Monad m, Traversable t) => t (a -> m b) -> a -> m (t b)

so for example (using the ClassyPrelude for full generality at the cost of some constraint noise, with headMay as a more general form of listToMaybe):

f :: (Traversable t, MonoFoldable (t b), Element (t b) ~ b)
  => t (a -> Maybe b) -> a -> Maybe b
f functions x = join $ headMay <$> swing mapM functions x

Yes, it might turn your head into mush - but it's like bending your head to see a smiley, only with your whole mind.

like image 125
MarLinn Avatar answered Oct 17 '22 20:10

MarLinn