Нет экземпляра для (Fractional Int), возникающего из-за использования `/'
Я новичок в Haskell, и я борюсь с отладкой своего кода.
Устранение ошибки приводит к другим ошибкам...
Вот мой код.
import Data.Maybe
data Op = Add | Sub | Mul | Div | And | Or | Not | Eq | Less | Great
deriving (Eq, Show)
data Exp = Literal Value
| Primitive Op [Exp]
| Variable String
| If Exp Exp Exp
| Let [(String, Exp)] Exp
deriving (Show, Eq)
data Value = Number Int
| Bool Bool
| String String
deriving (Eq, Show)
type Env = [(String, Value)]
eval :: Env -> Exp -> Value
eval e (Literal v) = v
eval e (Variable x) = fromJust (lookup x e) --22
prim :: Op -> [Value] -> Value
prim Add [Number a, Number b] = Number (a + b)
prim And [Bool a, Bool b] = Bool (a && b)
prim Sub [Number a, Nuamber b] = Number (a - b) -- No instance for (Fractional Int) arising from a use of `/'
prim Mul [Number a, Number b] = Number (a * b)
prim Div [Number a, Number b] = Number (a / b)
prim Or [Bool a, Bool b] = Bool (a || b)
prim Not [Bool a] = Bool (not a)
prim Eq [Number a, Number b] = Bool (a == b)
prim Eq [String a, String b] = Bool (a == b)
prim Less [Number a, Number b] = Bool (a < b)
prim Less [String a, String b] = Bool (a < b)
prim Great [Number a, Number b] = Bool (a > b)
prim Great [String a, String b] = Bool (a > b) --37
main = do
eval [("y", (Number 40))] (Let [("x", (Literal (Number 2)))] (prim Add [(Variable "x"), (Variable "y")])) -- Couldn't match expected type `Exp' with actual type `Value'
Теперь я получаю две ошибки, которые я написал в комментариях.
Если вы знаете, что не так с моим кодом, пожалуйста, поделитесь своей идеей и сэкономить мое время...
Большое спасибо.
Ответы
Ответ 1
-- No instance for (Fractional Int) arising from a use of `/'
Предположительно, исходящий из этой строки, а не с комментариями:
prim Div [Number a, Number b] = Number (a / b)
a
и b
являются Int
s. Оператор деления (/) :: Fractional a => a -> a -> a
(вы можете найти это, активировав ghci и введя :t (/)
, или глядя на Hoogle).
Если вы не видели такие типы, как Fractional a => a -> a -> a
, вы должны прочитать это в двух частях:
- Контекст
Fractional a
- Тип
a -> a -> a
Это похоже на обычный тип a -> a -> a
, поэтому он принимает два аргумента некоторого типа и возвращает результат одного и того же типа. Единственная разница в добавлении контекста Fractional a
заключается в том, что тип, используемый для a
, должен быть экземпляром класса типа Fractional
; он не является бесплатным для использования над любым типом, который вам нравится.
Если вы еще не узнали о типах классов, не беспокойтесь. Их довольно легко понять, но не то, что вы должны смотреть в глубину, когда только начинаете; вы доберетесь до них позже.
Int
не является членом класса типа Fractional
, поэтому оператор /
не работает на Int
s.
Причина в том, что регулярное математическое деление не работает на целые числа с этим типом; 3 / 2
должен был бы либо дать 1.5
, и в этом случае он не соответствует типу Int -> Int -> Int
, либо дает 1
или 2
, и в этом случае это не будет правильным математическим делением. Существует функция div
для реализации целочисленного деления, используемая как a `div` b
в нотации infix.
-- Couldn't match expected type `Exp' with actual type `Value'
Это сообщение о ваших собственных типах, в одном выражении, которое вы написали. И фактическое полное сообщение об ошибке предоставило бы вам больше контекста, какая часть выражения содержит ошибку. Просто следуйте ему сверху вниз, проверяя типы вещей самостоятельно, и ошибка очень быстро выскочит на вас.
В этом случае вы можете:
Let [("x", (Literal (Number 2)))] (prim Add [(Variable "x"), (Variable "y")])
Let
нужны два аргумента: a [(String, Exp)]
и Exp
. Список в порядке, но второй аргумент (prim Add [(Variable "x"), (Variable "y")])
. Даже не углубляясь в подструктуру этого, чтобы убедиться, что он правильный, prim
имеет тип Op -> [Value] -> Value
, поэтому он не сможет дать вам Exp
.
Как исправить это до вас; похоже, что вам нужно немного рефакторинга во всей разнице между выражением/значением. prim
предоставляет вам Value
, который вы можете просто применить к обертке в Literal
, чтобы пройти мимо ошибки типа, которую вы получаете, но затем вы столкнулись с проблемой, что prim
должен принимать Op
> и a [Value]
, но вы, кажется, дали ему Op
и [Exp]
(содержащие переменные). Я думаю, вам нужно подумать о различии между использованием prim
для вычисления результатов примитивного приложения, используя конструктор Primitive
Exp
для представления примитивного приложения и используя eval
для оценки (в среде ) произвольное выражение (которое может содержать несколько примитивных приложений) к значению.
Ответ 2
Проблема, с которой вы сталкиваетесь, заключается в том, что у Haskell есть разные функции для целочисленного и дробного деления. Целочисленное деление усекает, дробное деление - нет. Поэтому вместо
prim Div [Number a, Number b] = Number (a / b)
вы хотите сделать
prim Div [Number a, Number b] = Number (a `div` b)
В действительности сообщение об ошибке означает, что функция (/)
является частью класса Fractional
. Это в основном интерфейс, который могут реализовывать различные типы. Чтобы получить информацию об этом, запустите ghci и сделайте
Prelude> :i (/)
class Num a => Fractional a where
(/) :: a -> a -> a
...
-- Defined in `GHC.Real'
infixl 7 /
Prelude> :i Int
data Int = GHC.Types.I# GHC.Prim.Int# -- Defined in `GHC.Types'
instance Bounded Int -- Defined in `GHC.Enum'
instance Enum Int -- Defined in `GHC.Enum'
instance Eq Int -- Defined in `GHC.Classes'
instance Integral Int -- Defined in `GHC.Real'
instance Num Int -- Defined in `GHC.Num'
instance Ord Int -- Defined in `GHC.Classes'
instance Read Int -- Defined in `GHC.Read'
instance Real Int -- Defined in `GHC.Real'
instance Show Int -- Defined in `GHC.Show'
Первое дает вам информацию о функции (/)
: она сообщает вам, что она находится в классе Fractional
. Затем, когда вы :i Int
, вы увидите все экземпляры для Int
. Обратите внимание, что Int
не является экземпляром Fractional
, поэтому вы не можете использовать (/)
с Int
.
Другой совет: обратные элементы (`) превращают функцию в инфиксный оператор, поэтому
a `div` b
совпадает с
div a b