В Scala я мог бы написать следующее trait
:
trait Consumer[A] {
def apply(a: A): Unit
}
И scala конвертирует все, что я захочу в Unit
, т. Е. Оно будет отбрасывать тип. Эквивалентно, я мог бы сказать, что apply
возвращает Any
и игнорировать результат.
Однако в Haskell, если бы я определил тип как type Consumer = a -> IO ()
, я бы не смог передать Int -> IO Int
функция, так как Int
не ()
.
Существует два способа решения этой проблемы, но ни один не является удовлетворительным:
- Использование
Data.Functor.void
на сайте вызова для ручного изменения IO a
на IO ()
. Это раздражает как пользователя API. - define
type Consumer a b = a -> IO b
, но тогда каждый раз, когда я захочу использовать Consumer
в подписи, мне придется носить бесполезный тип b
.
Есть ли способ определить тип Consumer
как функцию от a
до "IO Any
"? Насколько я знаю, Haskell не поддерживает что-то вроде exists x. a -> IO x
.
Использование forall
приводит к противоположному тому, что я хочу, например,
type Consumer = forall b. a -> IO b
foo :: Int -> IO Int
foo = undefined
bar :: Consumer Int
bar = foo
приводит к ошибке:
• Couldn't match type ‘b’ with ‘Int’
‘b’ is a rigid type variable bound by
the type signature for:
bar :: Consumer Int
Expected type: Int -> IO b
Actual type: Int -> IO Int
• In the expression: foo
In an equation for ‘bar’: bar = foo
• Relevant bindings include
bar :: Int -> IO b
Обратите внимание, что я специально хочу Consumer
для псевдонима be type
, а не для конструктора data
, как описано здесь: Функция Haskell, возвращающая экзистенциальный тип ,Я бы не возражал, если бы Consumer
было class
, если бы кто-нибудь знал, как заставить это работать.