I am trying to parse a string with natural numbers in Agda.
e.g., the result of stringListToℕ "1,2,3" should be Just (1 ∷ 2 ∷ 3 ∷ [])
My current code is not quite right or by any means nice, but it works.
However it returns the type:
Maybe (List (Maybe ℕ))
The Question is:
-
How to implement the function
stringListToℕin a nice way (compared to my code);
it should have the typeMaybe (List ℕ) -
(optional, not important) How can I convert the type
Maybe (List (Maybe ℕ))toMaybe (List ℕ)?
My Code:
charToℕ : Char → Maybe ℕ
charToℕ '0' = just 0
charToℕ '1' = just 1
charToℕ '2' = just 2
charToℕ '3' = just 3
charToℕ '4' = just 4
charToℕ '5' = just 5
charToℕ '6' = just 6
charToℕ '7' = just 7
charToℕ '8' = just 8
charToℕ '9' = just 9
charToℕ _ = nothing
stringToℕ' : List Char → (acc : ℕ) → Maybe ℕ
stringToℕ' [] acc = just acc
stringToℕ' (x ∷ xs) acc = charToℕ x >>= λ n → stringToℕ' xs ( 10 * acc + n )
stringToℕ : String → Maybe ℕ
stringToℕ s = stringToℕ' (toList s) 0
isComma : Char → Bool
isComma h = h Ch.== ','
notComma : Char → Bool
notComma ',' = false
notComma _ = true
{-# NO_TERMINATION_CHECK #-}
split : List Char → List (List Char)
split [] = []
split s = l ∷ split (drop (length(l) + 1) s)
where l : List Char
l = takeWhile notComma s
isNothing' : Maybe ℕ → Bool
isNothing' nothing = true
isNothing' _ = false
isNothing : List (Maybe ℕ) → Bool
isNothing l = any isNothing' l
-- wrong type, should be String -> Maybe (List N)
stringListToℕ : String → Maybe (List (Maybe ℕ))
stringListToℕ s = if (isNothing res) then nothing else just res
where res : List (Maybe ℕ)
res = map stringToℕ (map fromList( split (Data.String.toList s)))
test1 = stringListToℕ "1,2,3"
-- => just (just 1 ∷ just 2 ∷ just 3 ∷ [])
EDIT
I tried to write a conversion function using from-just, but this gives a error when type checking:
conv : Maybe (List (Maybe ℕ)) → Maybe (List ℕ)
conv (just xs) = map from-just xs
conv _ = nothing
the error is:
Cannot instantiate the metavariable _143 to solution
(Data.Maybe.From-just (_145 xs) x) since it contains the variable x
which is not in scope of the metavariable or irrelevant in the
metavariable but relevant in the solution
when checking that the expression from-just has type
Maybe (_145 xs) → _143 xs
I took the liberty of rewriting your
splitfunction into something more general which also works with the termination check:Also,
stringToℕ ""should most likely benothing, unless you really want:Let’s rewrite it a bit (note that
helperis your originalstringToℕfunction):And now we can put it all together. For simplicity I’m using
List Chareverywhere, sprinkle withfromList/toListas necessary):You can find
sequenceinData.List; we also have to specify which monad instance we want to use.Data.Maybeexports its monad instance under the namemonad. Final code:And a small test:
Considering your second question: there are many ways to turn a
Maybe (List (Maybe ℕ))into aMaybe (List ℕ), for example:Right, this doesn’t do much. We’d like the conversion to preserve the elements if they are all
just.isNothingalready does this part of checking but it cannot get rid of the innerMaybelayer.from-justcould work since we know that when we use it, all elements of theListmust bejust xfor somex. The problem is thatconvin its current form is just wrong –from-justworks as a function of typeMaybe A → Aonly when theMaybevalue isjust x! We could very well do something like this:And since
from-listbehaves as aMaybe A → ⊤when givennothing, we are esentially trying to construct a heterogeneous list with elements of type both⊤andℕ.Let’s scrap this solution, I’ll show a much simpler one (in fact, it should resemble the first part of this answer).
We are given a
Maybe (List (Maybe ℕ))and we gave two goals:take the inner
List (Maybe ℕ)(if any), check if all elements arejust xand in this case put them all into a list wrapped in ajust, otherwise returnnothingsquash the doubled
Maybelayer into oneWell, the second point sounds familiar – that’s something monads can do! We get:
This function could work with any monad but we’ll be fine with
Maybe.And for the first part, we need a way to turn a
List (Maybe ℕ)into aMaybe (List ℕ)– that is, we want to swap the layers while propagating the possible error (i.e.nothing) into the outer layer. Haskell has specialized typeclass for this kind of stuff (TraversablefromData.Traversable), this question has some excellent answers if you’d like to know more. Basically, it’s all about rebuilding the structure while collecting the “side effects”. We’ll be fine with the version that works just forLists and we’re back atsequenceagain.There’s still one piece missing, let’s look at what we have so far:
We need to apply
sequence-maybeinside oneMaybelayer. That’s where theMaybefunctor instance comes into play (you could do it with a monad instance alone, but it’s more convenient). With this functor instance, we can lift an ordinary function of typea → binto a function of typeMaybe a → Maybe b. And finally: