自定义数据类型的Monadic Haskell运算符(带有状态的+)

Pau*_*aul 1 error-handling monads haskell

我正在按照48小时编写自己的方案教程,并给出下面的代码我稍微绕道而行,以便能够运行(+ 4 4.0)(我添加了对Floats的支持):

import Control.Monad.Except
import Text.ParserCombinators.Parsec hiding (spaces)

data LispError = NumArgs Integer [LispVal]
    | TypeMismatch String LispVal
    | Parser ParseError
    | BadSpecialForm String LispVal
    | NotFunction String String
    | UnboundVar String String
    | Default String

type ThrowsError = Either LispError

data LispVal = Atom String
    | List [LispVal]
    | DottedList [LispVal] LispVal
    | Number Integer
    | Float Float
    | String String
    | Bool Bool

instance Show LispVal where
    show = showVal

instance Show LispError where
    show = showErr

showVal :: LispVal -> String
showVal (Number x) = show x
-- ...

showErr :: LispError -> String
showErr (TypeMismatch expected found) = "Invalid type, expected: " ++ expected ++ ", found: " ++ show found
showErr (Default message) = "Error: " ++ message
-- ...

instance Num LispVal where
    (Number x) + (Number y) = Number $ x + y
    (Float x) + (Float y) = Float $ x + y
    (Number x) + (Float y) = Float $ (fromInteger x) + y
    (Float x) + (Number y) = Float $ x + (fromInteger y)

plusLispVal :: LispVal -> LispVal -> ThrowsError LispVal
(Number x) `plusLispVal` (Number y) = return . Number $ x + y
(Float x) `plusLispVal` (Float y) = return . Float $ x + y
(Number x) `plusLispVal` (Float y) = return . Float $ (fromInteger x) + y
(Float x) `plusLispVal` (Number y) = return . Float $ x + (fromInteger y)
x `plusLispVal` (Number _) = throwError $ TypeMismatch "number" x
x `plusLispVal` (Float _) = throwError $ TypeMismatch "number" x
(Number _) `plusLispVal` x = throwError $ TypeMismatch "number" x
(Float _) `plusLispVal` x = throwError $ TypeMismatch "number" x
x `plusLispVal` y = throwError $ Default $ "+ expects numbers, given: " ++ show x ++ " and " ++ show y
Run Code Online (Sandbox Code Playgroud)

我想知道我是否可以以某种方式使+操作符等效于plusLispVal上面的函数,也就是说,使它成为monadic所以我可以通过它传递错误状态,我认为这会使我的代码更清洁,我也可以从减法中受益(和其他操作)免费.

例:

*Main> (Number 2) + (String "asd")
*** Exception: asd.hs:(51,5)-(54,56): Non-exhaustive patterns in function +

*Main> (Number 2) `plusLispVal` (String "asd")
Left Invalid type, expected: number, found: "asd"
Run Code Online (Sandbox Code Playgroud)

Cub*_*bic 5

编号+具有类型Num a => a -> a -> a,即如果您的信息未包含在其中一个参数中,则它也不会出现在结果中.你可以做的就是解除它:liftM2 (+) :: (Monad m, Num a) => m a -> m a -> m a或者你可以引入一种看起来像是+你所追求的功能(+!) = plusLispVal.

你可能想要有一个升级版+ +,因为否则你不能链接添加(和其他操作)(另外,你的Num实例似乎缺乏fromIntegral实现).