【问题标题】:deriving Applicative in haskell在haskell中导出Applicative
【发布时间】:2021-05-04 13:56:46
【问题描述】:

GHC 为什么不为KO 派生Applicative

#!/usr/bin/env stack
-- stack --resolver lts-17.10 script

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype KO a = KO a deriving (Functor, Applicative) -- doesn't derive Applicative

newtype OK f a = OK (f a) deriving (Functor, Applicative) -- that's ok

main :: IO ()
main = print "hello"

• 无法创建“Applicative KO”的派生实例 (即使使用狡猾的 GeneralizedNewtypeDeriving): 无法充分减少表示类型 • 在“KO”类型检查的新类型声明中

【问题讨论】:

  • 如果你 :i Applicative 你会看到这个:type Applicative :: (* -> *) -> Constraint - 所以如果类型是 * -> * 则类型可以是适用的(container 是适用的 - 比如[]Maybe - 不是 [a]Maybe a) - 这就是它不起作用的原因 - 你的 newtype 是 any 类型的包装器 - 如果你要为 @ 创建一个实例987654331@ 你会做Applicative f => instance Applicative (OK f) (Ok f : * -> *) - 尝试自己为KO 编写一个实例 - 有一个(基本上是Identity - 查一下) - 但这不是你对新类型的期望包装器
  • 如果Identity 行为是您想要的,您可以使用DerivingVia 并说newtype KO a = KO a deriving (Functor, Applicative) via Identity
  • deriving via 更规律,更少惊喜
  • @Carsten, fwiw, :i Functor 也显示type Functor :: (* -> *) -> Constraint。那么为什么Functor 没有提出同样的问题呢?
  • @Enlico Functor 不会造成同样的问题,因为它使用了不同的实例生成方法。我的回答有一些细节和完整文档的链接。

标签: haskell


【解决方案1】:

GHC 有四种创建实例的方式:

  • stock:通过模式匹配等从头开始编写每个类的方法的新实现
  • newtype:当声明一个 newtype 包装已经有实例的类型时,重用该实例,在适当的时候插入和删除新类型包装器
  • anyclass:声明一个没有方法定义的实例(因此对每个方法都使用默认实现,如果有的话)
  • vianewtype 的概括,它允许您从具有明显相同表示的任何其他类型继承实例,再次通过在适当的时候插入/删除新类型包装器

在您的代码 sn-p 中,stock 方法用于派生 Functor,因为您已打开 DeriveFunctor。不过,目前没有 stock 派生 Applicativevia 方法只有在明确请求时才会触发,并且您还没有打开 DeriveAnyClass,所以剩下的唯一选项是 newtype,因此 GHC 尝试从包装类型继承实例。然后它就遇到了麻烦,因为Applicative应该是针对容器类型的,而包含的类型不是一个,所以它会报错。

这解释了您的第一个 sn-p 的 FunctorApplicative 的区别。对于Applicative 的第一个和第二个 sn-p 差异,我们只需要观察在展开第二个之后,我们确实有一个容器类型;因此,只要其 f 参数有一个,您就会得到一个 OKApplicative 实例。

另请参阅deriving strategies 上的文档。

【讨论】:

  • 我认为deriving (Functor) 会根据所涉及的newtype 的类型改变策略(这是有道理的)。我会发布一些例子
  • @nicolas 我链接的文档描述了 GHC 用来决定使用哪种策略的算法。
  • @DanielWagner via 不仅仅是newtype 的对偶。它包含newtype,本质上,要求只是原始类型和通孔类型在表示上是等价的。与原始类型相比,通孔类型是否包含在更多或更少的新类型包装器中是次要的...
  • 它描述它“如果可能的话”;)“股票:让 GHC 为数据类型实现一个“标准”实例,如果可能
  • @nicolas 下一部分是我所指的算法,并说明何时可能:“报告中指定的那些以及由语言扩展启用的那些”,“语言扩展”是一个链接到...好吧,它可能是为了链接here,它列出了扩展以及它们启用的类。
【解决方案2】:

除了答案之外,这里还有一些更详细的示例,突出了这两种行为。

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- deriving Functor
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

newtype F a = F {unF :: a} deriving (Functor)

-- translates to

newtype F' a = F' a

deriving instance Functor F' -- F' endowed with instance -- (stock strategy)

-- whereas

newtype HF f a = HF {unHF :: f a} deriving (Functor)

-- delegates to
newtype HF' f a = HF' (f a)

deriving instance Functor f => Functor (HF' f) -- HF' f delegates to f

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- deriving Applicative
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

-- -- commented for typecheck
-- newtype G a = G a deriving (Functor, Applicative)

-- -- translates to pb
-- newtype G' a = G' a

-- deriving instance Functor G'

-- deriving instance Applicative G' -- G' not endowed with instance

-- -- whereas

newtype HG f a = HG (f a) deriving (Functor)

-- delegates

newtype HG' f a = HG' (f a)

deriving instance Functor f => Functor (HG' f) -- delegates to f

deriving instance Applicative f => Applicative (HG' f) -- delegates to f

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

---

-- Endows with a Functor instance
-- >>>  unF $ (+ 1) <$> F 6
-- 7

-- Delegates the functor instance to f
-- >>> unHF $ (+1) <$> HF (Just 6)
-- Just 7

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

【讨论】:

  • 我发现这个 sn-p 相当具有误导性。在此处显示的所有Functor 案例中,您的“委托”是通过股票派生而不是通过广义的新类型派生发生的,这似乎是您所暗示的。
  • 我只是想强调差异,而不是在实现中如何出现这种差异
  • 您要强调原始问题中没有的区别是什么?
  • 这是对 两种行为的更对称表示。我没有暗示任何东西,或者至少我也尝试过(因此这里有更详细的代码..)
【解决方案3】:

为了说明DerivingVia 如何更明确地说明每个选项,这里是每个选项的翻译

#!/usr/bin/env stack
-- stack --resolver lts-17.10 script

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE StandaloneDeriving #-}

import Control.Monad.Identity
import Control.Monad.Trans.Maybe

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- Legacy
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

-- Endows with a Functor instance
newtype FStock a = FStock {unFStock :: a} deriving (Functor)

-- >>>  unFStock $ (+ 1) <$> FStock 6
-- 7

-- Delegates the functor instance to f
newtype HFDel f a = HFDel {unHFDel :: f a} deriving (Functor)

-- >>> unHFDel $ (+1) <$> HFDel (Just 6)
-- Just 7

-- bad = unHFDel $ (+ 1) <$> HFDel 6 -- error nothing to delegate to

deriving instance Applicative f => Applicative (HFDel f)

-- >>> unHFDel $ (HFDel (Just (+1))) <*> HFDel (Just 6)
-- Just 7

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- Deriving Via
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

newtype F a = F {unF :: a}

deriving via Identity instance Functor F

deriving via Identity instance Applicative F

-- Endows with instances, by explicitely delegating to the Identity
-- >>> unF $ (+ 1) <$> F 6
-- 7
-- >>> unF $ F (+ 1) <*> F 6
-- 7

newtype G f a = G {unG :: f a}

-- Delegates explicitely the instances to f
deriving via (f :: * -> *) instance (Functor f) => Functor (G f)

deriving via (f :: * -> *) instance (Applicative f) => Applicative (G f)

-- >>> unG $ (+1) <$> G (Just 6)
-- Just 7

-- bad = unG $ (+1) <$> G 6 -- error nothing to delegate to

-- >>> unG $ G (Just (+1)) <*> G (Just 6)
-- Just 7

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

main :: IO ()
main = do
  -- legacy
  print $ unFStock $ (+ 1) <$> FStock 6 -- 7
  print $ unHFDel $ (+ 1) <$> HFDel (Just 6) -- Just 7
  --print $ unHFDel $ (+ 1) <$> HFDel 6 -- error nothing to delegate to
  print $ unHFDel $ (HFDel (Just (+ 1))) <*> HFDel (Just 6) -- Just 7
  -- deriving via
  print $ unF $ (+ 1) <$> F 6 -- 7
  print $ unF $ F (+ 1) <*> F 6 -- 7
  print $ unG $ (+ 1) <$> G (Just 6) -- Just 7
  -- print $ unG $ (+ 1) <$> G 6 -- error nothing to delegate to
  print $ unG $ G (Just (+ 1)) <*> G (Just 6) -- Just 7

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-14
    • 1970-01-01
    • 2016-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多