【发布时间】:2017-12-05 12:40:34
【问题描述】:
这个问题与this one 有关,我想避免从数据结构中提取Id 值的样板,但以类型安全的方式。
这里我再重复一下问题的相关细节:假设你有一个类型Id:
newtype Id = Id { _id :: Int }
并且您想定义一个函数getId,从包含至少一个Id 值的任何结构中提取此Id:
class Identifiable e where
getId :: e -> Id
现在的问题是如何以类型安全的方式定义这样一个类,同时使用泛型避免样板。
在我的previous question 中,有人指出了类型族,尤其是described in this blog post 的想法。据我了解,这个想法是定义一个类型类MkIdentifiable,这样:
class MakeIdentifiable (res :: Res) e where
mkGetId :: Proxy res -> e -> Id
只有在其中嵌套了至少一个 Id 值时,一个值的类型为 Res:
data Crumbs = Here | L Crumbs | R Crumbs
data Res = Found Crumbs | NotFound
那么,似乎可以定义:
instance MakeIdentifiable (Found e) e => Identifiable e where
getId = mkGetId (Proxy :: Proxy (Found e))
现在的问题是如何为Res 定义与 GHC.Generics 的类型(U1、K1、:*:、:+:)相关联的类型族。
我尝试了以下方法:
type family HasId e :: Res where
HasId Id = Found Here
HasId ((l :+: r) p) = Choose (HasId (l p)) (HasId (r p))
Choose 与上述博文中的定义类似:
type family Choose e f :: Res where
Choose (Found a) b = Found (L1 a)
Choose a (Found b) = Found (R1 b)
Choose a b = NotFound
但这不会编译,因为HasId (l p) 有类型Res,而是需要一个类型。
【问题讨论】:
-
什么是
Choose? -
我添加了
Choose可能是什么的描述。
标签: haskell type-families ghc-generics