Ваша версия StateT выглядит правильно после быстрого взгляда. К сожалению, используя RMonad et. и др. требует дублирования почти всего; Я начал писать некоторый код с использованием «Подходящего» и сдался, потому что в нем было слишком много дублирования (и, в конце концов, оно мне действительно не понадобилось).
Edit:
Из моей памяти о том, как Suitable
работает, это примерно так:
Рассмотрим класс Functor
: Set
не может быть его экземпляром, так как ему требуется дополнительное ограничение Ord
.
Наивный подход будет выглядеть примерно так:
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
import qualified Data.Set as S
class MyFunctor c a | c -> a where
myfmap :: (MyFunctor c b) => (a -> b) -> c a -> c b
instance (Ord a) => MyFunctor S.Set a where
myfmap = S.map
Но тогда возвращается следующая ошибка:
Error: test.hs:11:16: Could not deduce (Ord b) arising from a use of `S.map'
from the context (Ord a)
bound by the instance declaration at /tmp/test.hs:10:10-37
or from (MyFunctor S.Set b)
bound by the type signature for
myfmap :: MyFunctor S.Set b => (a -> b) -> S.Set a -> S.Set b
at /tmp/test.hs:11:7-20
Possible fix:
add (Ord b) to the context of
the type signature for
myfmap :: MyFunctor S.Set b => (a -> b) -> S.Set a -> S.Set b
or the instance declaration
In the expression: S.map
In an equation for `myfmap': myfmap = S.map
In the instance declaration for `MyFunctor S.Set a'
Почему это? Это потому, что ограничение не сохраняется или не обнаруживается на уровне экземпляра, поэтому GHC не осознает, что при использовании myfmap
должно быть ограничение Ord
на b
.
Тип Suitable
используется для явного создания словаря ограничений, который позволяет указывать следующие виды ограничений:
import Data.Suitable
import qualified Data.Set as S
class MyFunctor c where
myfmap :: (Suitable c a, Suitable c b) => (a -> b) -> c a -> c b
instance MyFunctor S.Set where
myfmap f s = withConstraintsOf s
$ \ SetConstraints
-> withResConstraints $ \ SetConstraints -> S.map f s
(где SetConstraints
уже определено в Data.Suitable
)
Эта реализация работает, но она делает сигнатуры типов немного более замысловатыми, а реализации методов - немного более выразительными (обратите внимание, здесь нам нужно ввести SetConstraints
дважды : один раз для Set a
другой для Set b
). Обратите внимание, что для типов без каких-либо ограничений ни одна из функций ограничения Suitable
не требуется.
Я начал пытаться создавать Suitable
версии классов в Typeclassopedia, но сдался, потому что экземпляры становились довольно волосатыми.