haskell – Monad变压器用于发信号通知没有解决方案列表与`Nothing`

问候
Haskellers的同事.

这是我更大的约束满足问题的玩具版本
此刻正在努力.

下面的代码使用list monad转换器来表示给定的
正整数n作为不同的小偶数之和
方法.

import Control.Monad.Trans.List
import Control.Monad

sumToN' :: Int -> ListT Maybe [Int]
sumToN' n
  | n == 0 = pure []
  | otherwise = 
      do
        x <- ListT $Just [1..4]
        guard $x <= n
        guard $even x
        fmap (x:) $sumToN' $n - x

加载到GHCi后,该函数按预期工作.

λ> sumToN' 8
ListT (Just [[2,2,2,2],[2,2,4],[2,4,2],[4,2,2],[4,4]])
λ> sumToN' 7
ListT (Just [])

但是,我希望通过定义一个来为我的代码添加一些样式
如果没有解决方案,将返回ListT Nothing的函数
找到.这不是我得到的.我得到的结果可能是一个
如何为ListT定义mzero的结果.

λ> mzero :: ListT Maybe [Int]
ListT (Just [])

我的问题是:是否可以使用monad变换器进行组合
列表和可能,以便没有任何信号可以表明没有解决方案
码. (我不是在寻找黑客.我想知道monad
变形金刚会以某种方式直接支持这一点.)

作为背景信息,以下代码不使用monad
变压器根本就达到了与上述相同的结果
表示没有[]的解决方案.

sumToN :: Int -> [[Int]]
sumToN 0 = [[]]
sumToN n = do
  x <- [1..4]
  guard $x <= n
  guard $even x
  map (x:) $sumToN $n - x

这给了我们与列表变换器基本相同的结果.

λ> sumToN 8
[[2,2,2,2],[2,2,4],[2,4,2],[4,2,2],[4,4]]
λ> sumToN 7
[]

最佳答案 虽然我认为简单地删除Maybe是惯用的解决方案,但这里是你问的问题的答案.由于我们知道monad变换器堆栈的确切性质,因此我们可以使用模式匹配来对其值进行内省.

testList :: ListT Maybe a -> ListT Maybe a
testList (ListT (Just [])) = ListT Nothing
testList x = x

如果你想要一些与类型系统对话的东西,你可以在NonEmpty之外构建一个monad实例(请记住,这将不再是一个表现良好的monad变换器而不是ListT,但它会做你做的事情想要它.我们可以称之为NonEmptyT.

newtype NonEmptyT m a = NonEmptyT { unNonEmptyT :: m (NonEmpty a) }

-- Instance implementations omitted for brevity

instance Functor m => Functor (NonEmptyT m) where
    ...

instance Applicative m => Applicative (NonEmptyT m) where
    ...

instance Monad m => Monad (NonEmptyT m) where
    ...

instance MonadTrans NonEmptyT where
    ...

现在我们可以写了

testList' :: [a] -> NonEmptyT Maybe a
testList' [] = NonEmptyT Nothing
testList' (x:xs) = NonEmptyT $Just (x :| xs)

这需要一个普通的列表并将其转换为NonEmptyT Maybe.与以前的方法不同的是,你不喜欢的值(ListT(Just []))在我们的新类型中甚至没有意义.你可以拥有NonEmptyT Nothing,你可以拥有NonEmptyT(只是someNonemptyList),但是表达式NonEmptyT(Just [])甚至不会进行类型检查.

这不是你正在寻找的行为,变换器实际上与它下面的层相互作用并理解它,但我认为这是朝着正确方向迈出的一步,因为它彻底禁止程序员构建你不具备的价值想要存在

Full working example.

(脚注:不幸的是,GHC不想让我在这个新类型上使用GeneralizedNewtypeDeriving或DeriveFunctor,所以你实际上必须手工编写实例.如果有人知道为什么这些扩展在这种情况下失败,我会爱知道原因.)

点赞