最简单的答案是添加字段访问器:
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
. 您现在可以替换coerce
为underlying
which 为编译器提供一些指导,它会替换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
[]