нет других конструкторов, не записывая это в констрейнты foo?
data A a where
MkA :: Num a => {value :: a} -> A a
foo :: A a -> A a -> A a
foo a b = MkA (value a + value b)
==============================
error:
• No instance for (Num a) arising from a use of ‘MkA’
Possible fix:
add (Num a) to the context of
the type signature for:
foo :: forall a. A a -> A a -> A a
• In the expression: MkA (value a + value b)
In an equation for ‘foo’: foo a b = MkA (value a + value b)
foo a b = MkA (value a + value b)
^^^^^^^^^^^^^^^^^^^^^^^
Просто не задавать тип для foo. Компилятор как раз знает, что тип будет Num a => A a -> A a -> A a, поэтому ругается на неправильную сигнатуру.
а если усложнить задачу? :) тут сигнатуру убрать нельзя, она в классе написана class C c where foo :: a -> c a data A a where MkA :: Num a => a -> A a instance C A where foo a = MkA a =========================== a.hs:40:13: error: • No instance for (Num a) arising from a use of ‘MkA’ Possible fix: add (Num a) to the context of the type signature for: foo :: forall a. a -> A a • In the expression: MkA a In an equation for ‘foo’: foo a = MkA a In the instance declaration for ‘C A’ | 40 | foo a = MkA a |
Тут это уже не работает специально. foo в том виде, в каком вы её описали, должна работать для любого типа, а в случае MkA это не так. Решений два: или добавить Num a в сигнатуру foo, или убрать Num a из определения типа A (действительно ли он там нужен?). Может быть, меня кто-то поправит, указав ещё пути.
я колдую над чуть более сложным примером, там констрейнт для конструктора убрать не получится, имплементация на него завязана при этом у разных типов, для которых я хочу инстансы класса, эти констрейнты разные то есть хочется что-нибудь вроде instance Num a => C (A a) where ... но "a", как здесь, зафиксировать (так можно сказать?) нельзя, потому что тогда по кайндам не проходит - C хочет "* -> *", а у "A a" он просто "*"
В этом случае надо изменить foo. Всё-таки ограничения важны в типах, они не для красоты пишутся. Думаю, можно ещё как-то поколдовать с TypeFamilies или MultiParamTypeClasses, чтобы вынести задание Num в другую часть кода. Что-то вроде class Num a => C c a where foo :: a -> c a
посмотрю, спасибо!
{-# LANGUAGE GADTs, MultiParamTypeClasses, FlexibleInstances #-} class C c a where foo :: a -> c a data A a where MkA :: Num a => a -> A a instance Num a => C A a where foo = MkA Но меня всё равно не оставляет ощущение, что проще было бы добавить Num a как ограничение к foo или разобраться, что же на самом деле требуется для A.
проще в конкретном случае - конечно :) но может быть, например, библиотечный класс, который менять нельзя интересно разобраться, как такие штуки вообще решают (я ж точно не первый, кто таким вопросом задался) за сниппет спасибо! буду разбираться :)
Там ещё есть хитрость. Или такой класс: class C con c | c -> con where foo :: con a => a -> c a И сделать instance C Num A С типами аналогично. Можно, например, написать: data A n a where MkA :: n a => a -> A n a Правда, в обоих случаях надо не забыть включить нужные расширения.
вау, втащить констрейнты в параметры я б точно не догадался спасибо огромное!
Обсуждают сегодня