Пытаясь создать класс типов Vector
, который работает с кортежами, я столкнулся с некоторыми проблемами.
{-# LANGUAGE TypeFamilies, FlexibleInstances #-}
class Vector v where
type Scalar v :: *
vplus :: v -> v -> v
vmult :: v -> Scalar v -> v
vdot :: v -> v -> Scalar v
instance Num a => Vector (a, a) where
type Scalar (a,a) = a
(a, b) `vplus` (c, d) = (a + c, b + d)
(a, b) `vmult` m = (a * m, b * m)
(a, b) `vdot` (c, d) = a * c + b * d
Проблема в том, что мне нужны явные объявления типов для GHC, чтобы не запутаться. Это, конечно, небольшое неудобство, за исключением того, что vdot
, похоже, вообще не хочет работать.
res :: Int
res = (2, 3) `vdot` (5, 5)
-- error: Couldn't match expected type 'Int' with actual type 'Scalar (t0, t1)'
-- The type variables 't0', 't1' are ambiguous
Эта ошибка исчезнет, если я сделаю это:
res :: Int
res = ((2, 3) :: (Int, Int)) `vdot` (5, 5)
Но теперь мы достигли области многословного кода, настолько экстремального, что это уже просто непрактично. Haskell должен быть красивым и лаконичным; не явный тип ад
Я бы предположил, что GHC способен разрешить type Scalar (a, a) = a
самостоятельно, но ошибка сохраняется, даже если я полностью удаляю объявление экземпляра. Он даже жалуется, когда Vector (Int, Int)
является единственным доступным экземпляром.
Так что же здесь происходит? И есть ли способ заставить это работать красиво?
Scalar (a, a)
иVector (a, a)
, то единственным доступным типом являетсяa
. Так что, еслиa
является Int, каждый другой экземплярa
должен также быть Int 23.12.2015Vector (a, a)
. Дело в том, что нет ничего, что заставляло бы его выбирать, и на самом деле вы могли бы добавить второй, неперекрывающийся экземпляр, который также будет проверять тип проверки, если он используется вres
. GHC никогда не предполагает, что экземпляр будет выбран только потому, что он доступен, ему нужно найти экземпляр, используя логику, изложенную во второй половине моего ответа. 23.12.2015Vector (a, a)
может быть не единственным доступным экземпляром, который подойдет. На практике это означает, что компилятор не позволит вам писать программы, которые могут стать неоднозначными, если вы (или модули, которые вы импортируете) определяете дополнительные неперекрывающиеся экземпляры. 23.12.2015