我冒昧地重写了你的split
函数变成更通用的东西,也适用于终止检查:
open import Data.List
open import Data.Product
open import Function
splitBy : ∀ {a} {A : Set a} → (A → Bool) → List A → List (List A)
splitBy {A = A} p = uncurry′ _∷_ ∘ foldr step ([] , [])
where
step : A → List A × List (List A) → List A × List (List A)
step x (cur , acc) with p x
... | true = x ∷ cur , acc
... | false = [] , cur ∷ acc
Also, stringToℕ ""
最有可能的是nothing
,除非你真的想要:
stringListToℕ "1,,2" ≡ just (1 ∷ 0 ∷ 2 ∷ [])
让我们重写一下(注意helper
是你原来的stringToℕ
功能):
stringToℕ : List Char → Maybe ℕ
stringToℕ [] = nothing
stringToℕ list = helper list 0
where {- ... -}
现在我们可以把它们放在一起。为了简单起见,我使用List Char
到处撒上fromList
/toList
有必要的):
let x1 = s : List Char -- start
let x2 = splitBy notComma x1 : List (List Char) -- split at commas
let x3 = map stringToℕ x2 : List (Maybe ℕ) -- map our ℕ-conversion
let x4 = sequence x3 : Maybe (List ℕ) -- turn Maybe inside out
你可以找到sequence
in Data.List
;我们还必须指定要使用哪个 monad 实例。Data.Maybe
以名称导出其 monad 实例monad
。最终代码:
open import Data.Char
open import Data.List
open import Data.Maybe
open import Data.Nat
open import Function
stringListToℕ : List Char → Maybe (List ℕ)
stringListToℕ = sequence Data.Maybe.monad ∘ map stringToℕ ∘ splitBy notComma
还有一个小测试:
open import Relation.Binary.PropositionalEquality
test : stringListToℕ ('1' ∷ '2' ∷ ',' ∷ '3' ∷ []) ≡ just (12 ∷ 3 ∷ [])
test = refl
考虑你的第二个问题:有很多方法可以改变Maybe (List (Maybe ℕ))
into a Maybe (List ℕ)
, 例如:
silly : Maybe (List (Maybe ℕ)) → Maybe (List ℕ)
silly _ = nothing
是的,这并没有多大作用。我们希望转换能够保留元素(如果它们全部)just
. isNothing
已经做了这部分检查,但它无法摆脱内部Maybe
layer.
from-just
could之所以有效,是因为我们知道当我们使用它时,List
必须是just x
对于一些x
。问题是conv
目前的形式是错误的 -from-just
作为类型的函数Maybe A → A
仅当Maybe
值为just x
!我们很可以做这样的事情:
test₂ : Maybe (List ℕ)
test₂ = conv ∘ just $ nothing ∷ just 1 ∷ []
自从from-list
表现为Maybe A → ⊤
当给予nothing
,我们本质上是试图构建一个异构列表,其元素类型均为⊤
and ℕ
.
让我们放弃这个解决方案,我将展示一个更简单的解决方案(事实上,它应该类似于这个答案的第一部分)。
我们被赋予了一个Maybe (List (Maybe ℕ))
我们给出了两个目标:
嗯,第二点听起来很熟悉——这是 monad 可以做的事情!我们得到:
join : {A : Set} → Maybe (Maybe A) → Maybe A
join mm = mm >>= λ x → x
where
open RawMonad Data.Maybe.monad
这个函数可以与任何 monad 一起使用,但我们可以使用Maybe
.
对于第一部分,我们需要一种方法来转变List (Maybe ℕ)
into a Maybe (List ℕ)
- 也就是说,我们希望在传播可能的错误时交换层(即nothing
)进入外层。 Haskell 有专门的类型类来处理这类东西(Traversable
from Data.Traversable
), 这个问题如果您想了解更多信息,有一些很好的答案。基本上,这都是关于重建结构,同时收集“副作用”。我们会选择适合的版本List
s,我们回到了sequence
again.
还缺少一块,让我们看看到目前为止我们已经有了什么:
sequence-maybe : List (Maybe ℕ) → Maybe (List ℕ)
sequence-maybe = sequence Data.Maybe.monad
join : Maybe (Maybe (List ℕ)) → Maybe (List ℕ)
-- substituting A with List ℕ
我们需要申请sequence-maybe
里面一Maybe
层。那就是Maybe
functor 实例开始发挥作用(您可以单独使用 monad 实例来完成,但它更方便)。通过这个函子实例,我们可以提升类型的普通函数a → b
转化为类型的函数Maybe a → Maybe b
。最后:
open import Category.Functor
open import Data.Maybe
final : Maybe (List (Maybe ℕ)) → Maybe (List ℕ)
final mlm = join (sequence-maybe <$> mlm)
where
open RawFunctor functor