我正在通过我的方式“真实世界哈斯克尔”,而任务是使安全版本head, tail, last,和init. 我已经成功的前三,但Maybe类型类是杀死我的init。
这是我的代码:
-- safeInit
safeInit :: [a] -> Maybe [a]
safeInit [] = Nothing
safeInit (x:xs) = if null xs
then Just [x]
else x : (safeInit xs)
Run Code Online (Sandbox Code Playgroud)
这是加载到 GHCI 时产生的错误(该函数从原始文件的第 23 行开始:
[1 of 1] Compiling Main ( ch04.exercises.hs, interpreted )
> ch04.exercises.hs:27:26: error:
> • Couldn't match expected type ‘Maybe [a]’ with actual type ‘[a]’
> • In the expression: x : (safeInit xs)
> In the expression: if null xs then Just [x] else x : (safeInit xs)
> In an equation for ‘safeInit’:
> safeInit (x : xs) = if null xs then Just [x] else x : (safeInit xs)
> • Relevant bindings include
> xs :: [a] (bound at ch04.exercises.hs:25:13)
> x :: a (bound at ch04.exercises.hs:25:11)
> safeInit :: [a] -> Maybe [a] (bound at ch04.exercises.hs:24:1) | 27 | else x : (safeInit xs) |
> ^^^^^^^^^^^^^^^^^
>
> ch04.exercises.hs:27:31: error:
> • Couldn't match expected type ‘[a]’ with actual type ‘Maybe [a]’
> • In the second argument of ‘(:)’, namely ‘(safeInit xs)’
> In the expression: x : (safeInit xs)
> In the expression: if null xs then Just [x] else x : (safeInit xs)
> • Relevant bindings include
> xs :: [a] (bound at ch04.exercises.hs:25:13)
> x :: a (bound at ch04.exercises.hs:25:11)
> safeInit :: [a] -> Maybe [a] (bound at ch04.exercises.hs:24:1) | 27 | else x : (safeInit xs) |
> ^^^^^^^^^^^ Failed, no modules loaded.
Run Code Online (Sandbox Code Playgroud)
无论如何我用 标记或不标记最后两行的x或,我都会得到不同但非常相关的打字错误。我错过了在列表中使用 Maybe 类型的哪些微妙之处?xsJust
这不起作用的主要原因是您的表达式x : safeInit xs不会进行类型检查。确实,safeInit xsis a Maybe [a],但(:)有 type (:) :: a -> [a] -> [a],所以类型不匹配。
还有一个语义错误。如果null xs是True,那么你应该返回Just []而不是Just [x],因为 thenx是列表中的最后一个元素。
您可以使用fmap :: Functor f => (a -> b) -> f a -> f b(so for f ~ Maybe, fmapis fmap :: (a -> b) -> Maybe a -> Maybe b) 来更改包含在 a 中的值Just:
safeInit :: [a] -> Maybe [a]
safeInit [] = Nothing
safeInit [_] = Just []
safeInit (x:xs) = fmap (x:) (safeInit xs)Run Code Online (Sandbox Code Playgroud)
但这将导致在Just. 这也意味着对于无限列表,它将陷入无限循环。我们可以简单地检查列表是否至少包含一个元素,然后执行 init 逻辑作为我们包装在 a 中的函数的结果Just:
safeInit :: [a] -> Maybe [a]
safeInit [] = Nothing
safeInit (x:xs) = Just (go xs x)
where go [] _ = []
go (x2:xs) x = x : go xs x2Run Code Online (Sandbox Code Playgroud)