You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用类型类模拟Rust的Fn trait?解决Haskell动态函数类型问题

用Haskell实现类似Rust的动态函数类型方案

Rust中的函数抽象实现

Rust中没有专门的函数类型,而是通过Fn trait实现函数抽象,定义如下:

trait Fn<A: Tuple, R> {
    fn call(self, args: A) -> R;
}

我们可以用dyn Fn(A) -> R表示动态函数类型,这种方式兼顾了编译器优化与使用便利性。

示例代码:

// Rust 示例 😊

fn run_with_2(f: &dyn Fn(i32) -> i32) -> i32 {
    f.call(2)
}

struct SomeFnWrapper<A,B>(Box<dyn Fn(A) -> B>);

Haskell的局限

在Haskell这类函数式语言中,可用类型类定义类似的Fn抽象:

class Fn s a b where
    call :: s -> a -> b

但Haskell的类型类没有Self概念,无法实现dyn Fn这类动态函数类型,只能全程依赖泛型:

-- Haskell 示例 🤮

runWith2 :: Fn f Int Int => f -> Int
runWith2 f = call f 2

-- 在Haskell中无法创建`SomeFnWrapper`这样的类型

解决方案:借助forall实现动态函数包装

通过Haskell的全称量化forall,可以实现类型擦除,模拟出类似Rust的动态函数效果,核心思路是利用存在类型打包具体函数类型。

方式一:基于存在类型的包装器

需要开启ExistentialQuantification扩展:

{-# LANGUAGE ExistentialQuantification #-}

-- 定义存在类型包装器,擦除具体函数类型,只保留约束
data SomeFn a b = forall f. Fn f a b => SomeFn f

-- 为包装器实现Fn类型类,让它支持call方法
instance Fn (SomeFn a b) a b where
    call (SomeFn f) x = call f x

-- 对应Rust的SomeFnWrapper
newtype SomeFnWrapper a b = SomeFnWrapper (SomeFn a b)

-- 接收动态函数的runWith2实现
runWith2 :: SomeFn Int Int -> Int
runWith2 (SomeFn f) = call f 2

-- 测试示例
addOne :: Int -> Int
addOne x = x + 1

main :: IO ()
main = do
    let wrapped = SomeFn addOne
    print $ runWith2 wrapped  -- 输出3

方式二:基于秩2类型的简化写法

开启RankNTypes扩展后,可通过高阶函数直接实现类型擦除,无需额外定义存在类型:

{-# LANGUAGE RankNTypes #-}

-- 定义动态函数类型,通过forall实现类型擦除
type DynFn a b = forall r. (forall f. Fn f a b => f -> r) -> r

-- 接收DynFn的runWith2实现
runWith2' :: DynFn Int Int -> Int
runWith2' fn = fn (\f -> call f 2)

-- 使用示例
main' :: IO ()
main' = do
    let dynAddOne :: DynFn Int Int
        dynAddOne cont = cont addOne
    print $ runWith2' dynAddOne  -- 输出3

总结

通过存在类型(结合ExistentialQuantification扩展)或秩2类型(结合RankNTypes扩展),Haskell完全可以实现类似Rust中dyn Fn的动态函数类型,打破只能全程使用泛型的局限。

内容的提问来源于stack exchange,提问作者דניאל פ.ח.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:22:15