関数定義パターンに型シグネチャを適用することはできません。これは、構文的に正しい書き方です。
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
そのモジュライの 2 つの型が得られます。タイプを自分で定義して、より適切な名前を付けることもできます。
newtype Mod s a = Mod { residue :: a }
newtype Modulus s a = Modulus { modulus :: a }
これはさておき、これを実際に使用したい場合は、ocharles が言ったことをお勧めします:リフレクションライブラリを使用してください。