How to write a Traversable instance for function, in Haskell?

痴心易碎 提交于 2021-01-28 05:28:12

问题


How do I write the Traversable instance for ((->) a)?

I think I could do it, if I could generically unwrap an Applicative Functor:

instance Traversable ((->) k) where
  -- traverse :: (a -> f b) -> (k -> a) -> f (k -> b)
  -- traverse h t = ?
  -- h                     :: Applicative f => a -> f b
  -- t                     :: k -> a
  -- h . t                 :: k -> f b
  -- unwrap . h . t        :: k -> b
  -- pure $ unwrap . h . t :: f (k -> b)
  traverse h t = pure $ unwrap . h . t

unwrap :: (Functor f, Applicative f) => f a -> a
unwrap y@(pure x) = x

But, alas, GHC won't let me get away with that:

Parse error in pattern: pure

回答1:


Generally there is no such thing as unwrap, consider f being the list functor [] what should unwrap return for [_, _, _] or better yet for the empty list []? Similar thing with Maybe, suppose h is const Nothing, you would expect to get Nothing. But your line of thought would fail upon trying to unwrap the Nothing into a value a. You can notice that trying to apply pure (to re-pack the result in the functor) means that you expect the result to be always Just for Maybe functor, non-empty for [] etc.

There is little hope for Traversable instance for a reader functor ((->) k). While it is not proof, a good evidence in that direction is that such an instance is missing from the Prelude. Also to traverse a function and produce a final container ([] or Maybe) you would need to apply your function h to any thinkable output of the function, that is a lot of potential values, in general infinitely many.

Prelude> traverse (\n -> if n == 42 then Nothing else Just n) [1, 2, 3]
Just [1,2,3]
Prelude> traverse (\n -> if n == 42 then Nothing else Just n) [1..]
Nothing

suppose that k is Int, so the functor is Int ->, suppose you have a value g :: Int -> Int, let it be \n -> if n == 42 then 0 else n, suppose you wanted to traverse that value with the above function, that traversal would be Nothing if g outputs 42 for any input, but it doesn't. The traversal cannot know that though (it has no access to the code of the function), so it would have to try all outputs.

If k were finite, then you could traverse a function by tabulating it. After traversing the table you could possibly produce a result. This may not be what you are after but:

import Data.Char
import Data.Maybe
import Data.Word

instance ( Enum k, Bounded k ) => Foldable ((->) k) where
    foldMap h f = foldMap (h . f) domain

instance ( Enum k, Bounded k, Eq k ) => Traversable ((->) k) where
    traverse h f = fmap (\vs k -> fromJust $ k `lookup` zip domain vs) (traverse (h . f) domain)

domain :: ( Enum k, Bounded k ) => [k]
domain = enumFromTo minBound maxBound

tabulate :: ( Enum k, Bounded k ) => (k -> a) -> [(k, a)]
tabulate f = zip domain (map f domain)

f1 :: Bool -> Int
f1 b = if b then 42 else 666

f2 :: Ordering -> Char
f2 LT = 'l'
f2 EQ = 'e'
f2 GT = 'g'

f3 :: Word8 -> Bool
f3 n = fromIntegral n < 256

f4 :: Word16 -> Bool
f4 n = fromIntegral n < 256

main = do
    print (tabulate f1)
    print (tabulate <$> traverse (\n -> [n, 2*n]) f1)
    putStrLn ""
    print (tabulate f2)
    print (tabulate <$> traverse (\c -> [c, toUpper c]) f2)
    putStrLn ""
    print (tabulate f3)
    print (tabulate <$> traverse (\b -> if b then Just b else Nothing) f3)
    putStrLn ""
    print (tabulate <$> traverse (\b -> if b then Just b else Nothing) f4)



回答2:


But, alas, GHC won't let me get away with that:

It seems your error is that you tried to use a function (pure) as a pattern. Haskell only allows constructors to appear in patterns. So

unwrap (Just x) = x

is valid, while

unwrap (pure x) = x

is not.



来源:https://stackoverflow.com/questions/46332201/how-to-write-a-traversable-instance-for-function-in-haskell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!