Проблема, как указано в сообщении об ошибке, заключается в следующем:
(<*>) _ x = x
Давайте дадим первому аргументу имя для ясности и предоставим «дырку» для тела:
(<*>) f x = _
Затем компилятор сообщит нам:
/Users/jpurdy/Code/Safe.hs:24:17: error:
• Found hole: _ :: Safeness b
…
• Relevant bindings include
x :: Safeness a
f :: Safeness (a -> b)
(<*>) :: Safeness (a -> b) -> Safeness a -> Safeness b
Единственные значения, которые вы можете вернуть: Unsafe (_ :: b)
, Safe (_ :: b)
или VerySafe (_ :: b)
, поэтому вы должны указать b
; с помощью параметризации only вы можете получить b
, сопоставив f
, чтобы получить a -> b
, и сопоставив x
, чтобы получить a
, и применив функцию к Значение. Другими словами, вы не можете просто вернуть x
без изменений.
К сожалению, решение зависит от того, что вы хотите сделать. Вы можете пометить результат с помощью минимум безопасности двух аргументов:
VerySafe f <*> VerySafe x = VerySafe $ f x
VerySafe f <*> Safe x = Safe $ f x
VerySafe f <*> Unsafe x = Unsafe $ f x
Safe f <*> VerySafe x = Safe $ f x
Safe f <*> Safe x = Safe $ f x
Safe f <*> Unsafe x = Unsafe $ f x
Unsafe f <*> VerySafe x = Unsafe $ f x
Unsafe f <*> Safe x = Unsafe $ f x
Unsafe f <*> Unsafe x = Unsafe $ f x
И это проще, если вычесть параметр безопасности из вашего типа (в алгебре типов от * 1029 от * a + a + a до 3 × a ):
data Safety = Unsafe | Safe | VerySafe
deriving (Eq, Ord)
data Safeness a = Safeness Safety a
instance Applicative Safeness where
-- Combine safeties with ‘min’, or use
-- ‘deriving (Semigroup, Monoid) via (Min Safety)’
-- to use the Semigroup ‘<>’ operator.
Safeness s1 f <*> Safeness s2 x
= Safeness (min s1 s2) (f x)
-- Must use ‘VerySafe’ instead of ‘Safe’
-- since it’s the identity for ‘min’,
-- to satisfy the ‘Applicative’ laws.
-- (Or respectively ‘mempty’ = ‘maxBound’.)
pure = Safeness VerySafe
Или вы можете пофантазировать и использовать GADT для поднятия параметра безопасности до уровня типа, вышесказанного экземпляр Applicative
, но позволяет комбинировать только значения с одинаковой безопасностью:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
data Safety = U | S | V
data Safeness (s :: Safety) a where
VerySafe :: a -> Safeness 'V a
Safe :: a -> Safeness 'S a
Unsafe :: a -> Safeness 'U a
apply :: Safeness s (a -> b) -> Safeness s a -> Safeness s b
apply (VerySafe f) (VerySafe x) = VerySafe (f x)
apply (Safe f) (Safe x) = Safe (f x)
apply (Unsafe f) (Unsafe x) = Unsafe (f x)
-- (Other cases are impossible because ‘s’ won’t match.)
В качестве дополнения вы можете использовать расширение DeriveFunctor
для записи deriving (Functor)
для Safeness
.