问题
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