Подтвердить что ты не робот

Нет экземпляра для (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'

Теперь я получаю две ошибки, которые я написал в комментариях. Если вы знаете, что не так с моим кодом, пожалуйста, поделитесь своей идеей и сэкономить мое время...

Большое спасибо.

4b9b3361

Ответ 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