是否有创建 [Maybe a] 模拟的语法,但类型 (* -> *)

问题描述

我试图创建一个适用于 [Maybe a] 的 fmap。然而,Maybe a 有 kind *,而 fmap 要求 kind * -> *。这导致了以下不幸的解决方案:

newtype Unfortunate a = Unfortunate ([Maybe a]) deriving Show

instance Functor Unfortunate
    where fmap f (Unfortunate a) = Unfortunate $ (fmap (fmap f)) a


-- |
-- >>> l = Unfortunate [Just 10,Just 1,nothing,Just 15]
-- >>> fmap (*5) l
-- Unfortunate [Just 50,Just 5,Just 75]

不幸的是必须创建一个 newtype。我希望可以为任何 [Maybe a] 创建一个适用于 a 的实例。即,可以称为 fmap f [Just 10,nothing] 的东西。

我似乎遗漏了一些语法。是否可以定义这样的实例?

解决方法

MaybeT 与您的 Unfortunate 所做的完全一样(甚至更多):

λ> import Control.Monad.Trans.Maybe
λ> (*2) <$> MaybeT [Just 1,Just 2,Nothing]
MaybeT [Just 2,Just 4,Nothing]
λ> pure 1 :: MaybeT [] Int
MaybeT [Just 1]

您也可以使用 DeriveFunctor 标志并机械派生“不幸的函子”:

λ> :set -XDeriveFunctor 
λ>  newtype Unfortunate a = Unfortunate ([Maybe a]) deriving (Functor,Show)
λ> (*2) <$> Unfortunate [Just 1,Nothing]
Unfortunate [Just 2,Nothing]
λ> 

如果你想走泛型路线,还有 generic-functor

编辑:

另一种选择是使用 GeneralizedNewtypeDeriving:

λ> :set -XGeneralizedNewtypeDeriving 
λ> (*2) <$> Unfortunate (MaybeT [Just 1,Nothing])
Unfortunate (MaybeT [Just 2,Nothing])

编辑 2:

正如 Daniel Wagner 在评论中指出的那样,我们也可以使用:

λ> import Data.Functor.Compose
λ> (*2) <$> Compose [Just 1,Nothing]
Compose [Just 2,Nothing]
,

不幸的是 newtype 的创建。我希望可以创建一个适用于 [Maybe a] 的实例,适用于任何 a。

已经有这样的例子了。它有效,不适用于任何 [Maybe a],但适用于任何 [a]。由于 a 可以与 Maybe a 统一,因此您也可以将其用于 [Maybe a]

现在,这显然不是你的意思。当专门用于此类型时,fmap 具有类型

(Maybe a -> Maybe b) -> [Maybe a] -> [Maybe b]

但您希望它采用 (a -> b) 类型的函数代替。我提出这个是为了解释为什么不能避免使用单独的类型1。您想要的类型已经有一个实例,因此您不能只定义一个新实例来替换该行为。您需要一个不同的类型来将该实例附加到。

正如在另一个答案中所讨论的,已经定义了泛型类型,可以组合这些类型以获得所需的设置。但是您不能只使用一个裸 [Maybe a]


1 您可以打开一些语言扩展来破坏类型系统以允许这样做,但这可能会产生令人惊讶的后果,我不会在这里讨论。

,

这是 DerivingVia 的完美用例(我注意到我没有足够仔细地阅读问题,您必须以一种或另一种方式使用 newtype:this解决方案不是您所希望的,但它是惯用的)

{-# Language DerivingVia              #-}
{-# Language StandaloneKindSignatures #-}

import Control.Applicative (Alternative)
import Control.Monad (MonadPlus)
import Control.Monad.Fix (MonadFix)
import Control.Monad.Trans.Maybe
import Control.Monad.Zip (MonadZip)
import Data.Kind (Type)

type    Fortunate :: Type -> Type
newtype Fortunate a = Fortunate [Maybe a]
 deriving
  ( Functor,Foldable,Applicative,Alternative,Monad,MonadPlus,MonadFail,MonadFix,MonadZip
  )
 via MaybeT []

其中一些实例(如 (Functor,Alternative))可以通过 Compose Maybe [] 派生。

要列出可以派生的实例,请使用 :instances 命令

>> :instances MaybeT []
instance [safe] Alternative (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Applicative (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
...
>> :instances Compose Maybe []
instance Alternative (Compose Maybe [])
  -- Defined in ‘Data.Functor.Compose’
instance Applicative (Compose Maybe [])
  -- Defined in ‘Data.Functor.Compose’
...

因为 Fortunate 是一个应用,你可以通过逐点(惯用的、应用的)提升推导出 (Semigroup,Monoid,Num,Bounded)

import Data.Monoid (Ap(..))

..
 deriving (Semigroup,Bounded)
 via Ap Fortunate a

其中 (<>) = liftA2 (<>)abs = liftA absmempty = pure memptyminBound = pure minBound