问候
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 [])甚至不会进行类型检查.
这不是你正在寻找的行为,变换器实际上与它下面的层相互作用并理解它,但我认为这是朝着正确方向迈出的一步,因为它彻底禁止程序员构建你不具备的价值想要存在
(脚注:不幸的是,GHC不想让我在这个新类型上使用GeneralizedNewtypeDeriving或DeriveFunctor,所以你实际上必须手工编写实例.如果有人知道为什么这些扩展在这种情况下失败,我会爱知道原因.)