您不能将类型签名应用于函数定义模式。这是语法正确的写法:
normalize :: (Modular s a, Integral a) => a -> M s a
normalize a = M (mod a (modulus (__ :: s))) :: M s a
但是,这行不通。您真正想要的是在函数的类型签名中引用类型变量s 。这可以通过使用 ScopedTypeVariables 扩展来完成,这需要显式量化:
normalize :: forall a s. (Modular s a, Integral a) => a -> M s a
normalize x = M (Mod x (modulus (__ :: s)))
作为改进代码的建议,我建议使用标记库:
import Data.Proxy
modulus :: (Modular s a) => Proxy s -> a
这使您可以在没有丑陋的占位符底部的情况下相处。另一种写法是:
modulus :: (Modular s a) => Tagged s a
这也给你一个很好的概念上的好处:你现在有两种类型,Mod
用于模块化值和Tagged
它们的模数。你也可以自己定义类型,给它一个更好的名字:
newtype Mod s a = Mod { residue :: a }
newtype Modulus s a = Modulus { modulus :: a }
抛开这一切不谈,如果你想实际使用它,我推荐 ocharles 所说的:使用反射库。