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

Haskell:如何在where子句中引用函数签名的类型变量?

问题:Haskell where子句中如何引用外层函数的类型变量?

先看这段可正常运行的代码:

data Test a = Test {
    sample    :: [a],
    underflow :: a,
    width     :: a,
    bounds    :: Int -> a
}

test :: Num a => [a] -> a -> a -> Test a
test sample underflow width = Test {
    sample,
    underflow,
    width,
    bounds
}
    where
        -- bounds :: Int -> a
        bounds 0 = underflow
        bounds i = width + bounds (i - 1)

main :: IO ()
main = print $ map (bounds t) [1..10]
    where t = test [1..6] 3 5

一旦取消注释where子句里的bounds :: Int -> a,代码就编译失败——因为这里的a是一个全新的类型变量,和test函数签名里的a完全不相关,编译器会认为你要给bounds指定一个和外层无关的类型,自然报错。

解决方法:使用ScopedTypeVariables扩展

这是最直接的解决方案,让内层where子句的类型变量能引用外层函数的类型变量:

  1. 在文件顶部开启扩展:
{-# LANGUAGE ScopedTypeVariables #-}
  1. 修改test函数的类型签名,用forall显式量化类型变量a,明确它的作用域:
test :: forall a. Num a => [a] -> a -> a -> Test a
  1. 现在就可以取消注释bounds的类型签名了,此时这里的a和test里的a是同一个类型变量。

修改后的完整代码:

{-# LANGUAGE ScopedTypeVariables #-}

data Test a = Test {
    sample    :: [a],
    underflow :: a,
    width     :: a,
    bounds    :: Int -> a
}

test :: forall a. Num a => [a] -> a -> a -> Test a
test sample underflow width = Test {
    sample,
    underflow,
    width,
    bounds
}
    where
        bounds :: Int -> a
        bounds 0 = underflow
        bounds i = width + bounds (i - 1)

main :: IO ()
main = print $ map (bounds t) [1..10]
    where t = test [1..6] 3 5

原理说明

Haskell默认规则下,每个类型签名里的类型变量都是“局部”的,where子句里的类型签名会被当成独立的,所以需要ScopedTypeVariables扩展打破这个限制,通过forall把类型变量的作用域扩展到整个函数(包括where子句),这样内层就能复用外层的类型变量了。

内容的提问来源于stack exchange,提问作者Chirmol Studio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 05:05:15