我正在关注Write Yourself a Scheme in 48 Hours教程并给出下面的代码我稍微绕道而行,以便能够运行(+ 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
我想知道我是否能以某种方式使+
运算符等同于上面的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"
答案 0 :(得分:5)
没有。 +
的类型为Num a => a -> a -> a
,即如果您的信息未包含在其中一个参数中,则它也不会出现在结果中。您可以做的事情是解除它:liftM2 (+) :: (Monad m, Num a) => m a -> m a -> m a
,或者您可以引入一个类似于+
的函数,如果这是(+!) = plusLispVal
之后的那个。 / p>
你可能想要提升+版本,否则你无法链接添加(和其他操作)(同样,你的Num
实例似乎缺少fromIntegral
实现)
答案 1 :(得分:0)
是的,通过重构代码以便LispError
可以放在LispVal
中,也许可以通过添加如下构造函数来实现:
data LispVal = Atom String
| List [LispVal]
| DottedList [LispVal] LispVal
| Number Integer
| Float Float
| String String
| Bool Bool
| Error LispError
然后,您可以为Num
撰写LispVal
个实例。
或者,您可以为Num
编写ThrowsError LispVal
个实例,并像return x + return y
一样使用它。