如何在Haskell类型族中支持多态类型?
解决Haskell类型族中的多态类型限制问题
你遇到的核心问题是GHC不允许在类型族的关联类型中直接使用带forall的多态类型——关联类型必须是单态的,不能包含顶层的多态量词。不过有几种可行的变通方案能实现你的需求:
方案1:用newtype包装多态类型
把多态函数包装在一个新的newtype里,绕过顶层forall的限制:
{-# LANGUAGE RankNTypes, TypeFamilies, FlexibleInstances #-} data Zero = Zero deriving (Show) newtype Succ x = Succ x deriving (Show) newtype PolyTuple2 = PolyTuple2 (forall v1 v2. v1 -> v2 -> (v1, v2)) class MakeTuple num where type TupleOf num makeTuplep :: TupleOf num instance MakeTuple (Succ (Succ Zero)) where type TupleOf (Succ (Succ Zero)) = PolyTuple2 makeTuplep = PolyTuple2 (,)
使用时通过模式匹配取出内部的多态函数:
unPolyTuple2 :: PolyTuple2 -> (forall v1 v2. v1 -> v2 -> (v1, v2)) unPolyTuple2 (PolyTuple2 f) = f -- 调用示例 test = unPolyTuple2 makeTuplep 1 "hello" -- 输出 (1,"hello")
方案2:用多参数类+函数依赖
放弃让类型族返回多态类型,转而给类添加额外类型参数,结合函数依赖实现推导:
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, FunctionalDependencies #-} data Zero = Zero deriving (Show) newtype Succ x = Succ x deriving (Show) class MakeTuple num t | num -> t where makeTuplep :: t instance MakeTuple (Succ (Succ Zero)) (v1 -> v2 -> (v1, v2)) where makeTuplep = (,)
函数依赖num -> t让GHC能根据num的类型推导出t的多态类型,调用时可通过类型应用指定num:
test = makeTuplep @(Succ (Succ Zero)) 1 "hello" -- 输出 (1,"hello")
方案3:结合AllowAmbiguousTypes简化实现
直接把多态性转移到类方法上,配合AllowAmbiguousTypes和TypeApplications:
{-# LANGUAGE AllowAmbiguousTypes, TypeFamilies, FlexibleInstances, TypeApplications #-} data Zero = Zero deriving (Show) newtype Succ x = Succ x deriving (Show) class MakeTuple num where makeTuplep :: forall v1 v2. v1 -> v2 -> (v1, v2) instance MakeTuple (Succ (Succ Zero)) where makeTuplep = (,)
调用时通过类型应用指定对应的自然数类型:
test = makeTuplep @(Succ (Succ Zero)) 1 "hello"
这些方案都能绕过原有的“非法多态类型”限制,核心思路是要么包装多态类型,要么将多态性从类型族转移到类方法层面。
内容的提问来源于stack exchange,提问作者Ashok Kimmel
相关产品推荐
相关产品推荐

