签名应为:
tailMay :: Foldable f => f a -> Maybe (f a)我得到了headMay的这个定义,这很有意义,而且(对我来说)相当聪明:
headMay :: Foldable f => f a -> Maybe a
headMay = foldr (const . Just) Nothing不幸的是,我还没能为tailMay想出任何类似的东西,而且similar implementations似乎使用了魔法或不安全的函数。
如果这是不可能的,那么对于我的目的来说,在(Applicative t, Foldable t)上工作的东西也是可以的(我猜这也意味着Traversable t)。
发布于 2019-11-21 23:35:06
Tail只对类似list的结构有意义,所以返回一个list是明智的,而不是返回相同的容器类型f。实现是微不足道的,可以用很多方法来完成,这里就是其中之一。
import Control.Monad (guard)
import Data.Foldable (toList)
tailMay :: Foldable f => f a -> Maybe [a]
tailMay xs = guard (not (null xs)) >> Just (drop 1 (toList xs))但是,因为它适用于所有类似列表的结构,所以我们可以很酷地为所有具有IsList的东西定义它。
{-# LANGUAGE TypeFamilies #-}
import Control.Monad (guard)
import Data.Foldable (toList)
import GHC.Exts (IsList(Item, fromList))
tailMay :: (IsList l, Item l ~ a, Foldable f) => f a -> Maybe l
tailMay xs = guard (not (null xs)) >> Just (fromList (drop 1 (toList xs)))编辑
上述解决方案的一个很好的好处是您可以将类型从参数更改为结果。例如,您可以从Vector开始,以Set作为结果。但这也有不利的一面,您需要自己指定结果类型:
因此,运行此命令将导致编译错误:
λ> tailMay [1 :: Int]
<interactive>:24:1-18: error:
• Illegal equational constraint Item l ~ Int
(Use GADTs or TypeFamilies to permit this)
• When checking the inferred type
it :: forall l. (IsList l, Item l ~ Int) => Maybe l
λ> tailMay [1 :: Int] :: Maybe [Int]
Just []我们可以很容易地限制结果类型,如果上面对我们来说真的是一个问题或仅仅是一个不想要的效果:
tailMay :: (IsList l, Item l ~ a, Foldable f, l ~ f a) => f a -> Maybe l在那之后,它工作得很好:
λ> import qualified Data.Vector as V
λ> tailMay $ V.enumFromTo 'a' 'd'
Just "bcd"
λ> tailMay [1 :: Int]
Just []
λ> tailMay V.empty
Nothing
λ> tailMay []
Nothing警告-这一切都很棒,但不幸的是,不能保证fromList会有一个完整的实现:
λ> import qualified Data.List.NonEmpty as NE
λ> tailMay (5 NE.:| [] :: NE.NonEmpty Int)
Just *** Exception: NonEmpty.fromList: empty list除了创建自己的类似IsList的类并为所有这样的数据结构提供自己的实例之外,没有通用的方法来保护自己不受它的影响。
发布于 2019-11-22 03:28:22
我不确定这个问题是否有一个正确的答案,尽管我想扩展@DanielWagner的评论,因为它可能是最好的选择,同时也是安全的:
-- | Law: (fromJust (length <$> tailMay l)) == (length tailMay) - 1,
-- if tailMay returns Just
class TailMay l where
tailMay :: l a -> Maybe (l a)
instance TailMay NonEmpty where
tailMay = LNE.nonEmpty . LNE.tail
instance TailMay [] where
tailMay = liftMay tail null -- <- from 'safe' package
-- from 'safe' package
liftMay :: (a -> b) -> (a -> Bool) -> (a -> Maybe b)
liftMay func test val = if test val then Nothing else Just $ func val也就是说,理想情况下,我也会更多地探索@dfeuer的评论。
https://stackoverflow.com/questions/58977458
复制相似问题