2

我试图用newtypewith来简化我的生活coerce,但是在某些情况下使用它时遇到了一个非常痛苦的问题:

import Data.Coerce (coerce)
import Data.Foldable (toList)

-- | newtype instance wrapping foldable type (not necessary [Int])
newtype Foo = Foo [Int]

bar :: [Int]
bar = toList $ coerce $ Foo [1,2,3]

这失败了,因为 Haskell 无法正确推断出类型。

有没有办法强制正确解决这个问题?(没有直接指定应该强制进入什么强制)或者也许还有其他干净的解决方案?(所以答案主要建议强制)

4

2 回答 2

3

lens您可能正在寻找的解决方案是Control.Lens.Wrapped

{-# LANGUAGE DeriveGeneric, StandaloneDeriving #-}

import Control.Lens
import Control.Lens.Wrapped
import GHC.Generics
import Data.Foldable (toList)

newtype Foo = Foo [Int] deriving (Generic)
instance Wrapped Foo

bar :: [Int]
bar = toList . view _Wrapped' $ Foo [1,2,3]
于 2022-02-03T18:26:29.803 回答
2

最简单的答案是添加字段访问器:

type    Foo :: Type
newtype Foo = Foo { getFoo :: [Int] }

bar :: [Int]
bar = toList $ getFoo $ Foo [1,2,3]

即使您Foo通过Foldable结构参数化它也有效:

-- >> toList $ getFoo $ Foo [1,2,3]
-- [1,2,3]
-- >> toList $ getFoo $ Foo Nothing
-- []
type    Foo :: (Type -> Type) -> Type
newtype Foo f = Foo { getFoo :: f Int }

如果您确实需要指导coerce,您可以想象一个解析基础类型的类型族。这是一个独立的类型系列,与Wrapped镜头答案中的关联不同。我个人认为这个类型家族应该包含在标准库中,并由 GHC 神奇地生成实例。

type          Underlying :: Type -> Type
type family   Underlying a
type instance Underlying Foo = [Int]
type instance Underlying Any = Bool
type instance Underlying All = Bool
-- ..

underlying :: Coercible a (Underlying a) => a -> Underlying a
underlying = coerce

bar :: [Int]
bar = toList $ underlying $ Foo [1,2,3]

我们不需要定义单独underlying的展开,所有的定义都是coerce. 您现在可以替换coerceunderlyingwhich 为编译器提供一些指导,它会替换getFoo; 即使在参数化的情况下:

type instance Underlying (Foo f) = f Int

-- >> toList $ underlying $ Foo [1,2,3]
-- [1,2,3]
-- >> toList $ underlying $ Foo Nothing
-- []

没有类型族来告诉目标类型是什么coerce太笼统了。有任意多种类型可以强制到/从Foo

coerce
  :: Foo -> [Int]
  :: Foo -> Identity [Int]
  :: Foo -> Identity (Sum (Product (Alt Identity (Ap [] Int))))
  :: ..

由于这个推理问题,我认为原始coerce出现在代码中是一种反模式,并尝试用其他抽象来替换它,GeneralizedNewtypeDeriving或者DerivingVia在我可以的时候。

TypeApplications话虽这么说,您可以在明确指定返回类型的地方握住 GHC 的手

>> toList $ coerce @_ @[Int] $ Foo [1,2,3]
[1,2,3]
>> toList $ coerce @_ @(Maybe Int) $ Foo Nothing
[]

或指定输入类型toList

>> toList @[] @Int $ coerce $ Foo [1,2,3]
[1,2,3]
>> toList @Maybe @Int $ coerce $ Foo Nothing
[]
于 2022-02-03T21:06:51.537 回答