使用GHC扩展ExistentialQuantification遇类型不匹配错误求解
咱们来拆解下你遇到的这个错误:本质是存在类型的“隐藏”特性在搞鬼。你定义的Func用forall s包裹了内部的s,这意味着这个s对外部代码来说是完全不透明的——没人知道它具体是什么类型,它可以是任意类型。
但你的test函数签名里的s是一个全称量词的类型变量,意思是调用者可以指定任意s类型,要求函数能处理这个类型的输入。这两个s根本不是一回事,GHC当然会报类型不匹配的错误。
那怎么让它们“对齐”呢?有两种常见的解决思路:
思路1:给存在类型添加约束或携带处理逻辑
既然外部不知道Func里的s是什么,那我们可以把处理这个s的逻辑直接打包到Func里,或者给s加上类型约束,让它支持我们需要的操作。
比如,修改Func的定义,让它同时携带转换函数和数据:
{-# LANGUAGE ExistentialQuantification #-} data Func a = forall s. Func (s -> a) s test :: Func a -> a test (Func f s) = f s
这样Func里已经包含了如何把内部的s转换成a的逻辑,test直接调用就行,完全不需要关心s的具体类型。
如果你的操作属于某个类型类(比如Show),也可以给存在类型加上类型约束:
{-# LANGUAGE ExistentialQuantification #-} data ShowableFunc = forall s. Show s => Func s testShow :: ShowableFunc -> String testShow (Func s) = show s
这里Func里的s必须是Show类型类的实例,所以testShow可以安全地调用show。
思路2:让处理函数支持任意类型(使用RankNTypes)
如果不想修改Func的定义,那你需要让test接受的函数能处理任意类型的输入——也就是一个多态函数。这时候需要开启RankNTypes扩展:
{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE RankNTypes #-} data Func = forall s. Func s test :: (forall s. s -> a) -> Func -> a test f (Func s) = f s
这里的(forall s. s -> a)表示f必须能接受任何类型的s作为输入,比如const 42(不管输入什么都返回42)这样的函数就符合要求。因为你不知道Func里的s到底是什么,所以只有这种能处理所有类型的函数才能安全调用。
总结一下:存在类型的核心是隐藏内部类型,所以外部代码不能假设它的类型是什么,要么把处理逻辑打包进去,要么用能处理任意类型的函数来操作它。
内容的提问来源于stack exchange,提问作者wind2412

