问候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
的函数
找到。这不是我得到的。我得到的结果可能是一个
为mzero
定义ListT
的结果。
λ> mzero :: ListT Maybe [Int]
ListT (Just [])
我的问题是:是否可以使用monad变换器进行组合
列表和Maybe
,以便Nothing
表示没有解决方案
代码。(我不是在寻找黑客。我想知道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
[]
答案 0 :(得分:1)
虽然我认为简单地删除Maybe
是惯用的解决方案,但这里是您要问的问题的答案。由于我们知道monad变换器堆栈的确切性质,我们可以使用模式匹配来反省它的值。
testList :: ListT Maybe a -> ListT Maybe a
testList (ListT (Just [])) = ListT Nothing
testList x = x
如果你想要一些与类型系统对话的东西,你可以用NonEmpty
构建一个monad实例(请记住,这不再是一个表现良好的monad变换器而不是{{} 1}}是,但它会做你想要的)。我们可以称之为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 (Just someNonemptyList)
甚至不会进行类型检查。
这并不是你正在寻找的行为,变压器实际上与它下面的层相互作用并理解它,但我认为它是朝着正确方向迈出的一步,因为它完全是正确的禁止程序员构建你不想存在的价值。
(脚注:不幸的是,GHC并不想让我在这个新类型上使用NonEmptyT (Just [])
或GeneralizedNewtypeDeriving
,所以你实际上必须手工编写实例。如果有人知道为什么这些扩展在这种情况下失败了,我很想知道原因。)