Syntax for list literals - agda

In Agda, what's the preferred way to write list literals, especially with multiple elements?
The best I could come up with is:
open import Data.Nat
open import Data.List
zeroElements oneElement multipleElements : List ℕ
zeroElements = []
oneElement = [ 0 ]
multipleElements = 0 ∷ 1 ∷ 2 ∷ []
(Note that [ 0 ] needs spaces inside the square brackets. Also note that ∷ is Unicode U+2237, not an ASCII double colon.)
I was hoping that the square bracket syntax would work with multiple elements, like [ 0 , 1 , 2 ], but this doesn't seem to be the case. Am I reading issue #235 correctly that there is no more concise syntax for this right now?

This is what I came up with before noticing you were linking to an issue
on the bug tracker and it's no improvement over the status quo at the time
module ListLiteral where
open import Agda.Builtin.List
infix 10 [_
infixr 15 _,_
infix 20 _]
pattern [_ x = x
pattern _,_ x xs = x ∷ xs
pattern _] x = x ∷ []
open import Agda.Builtin.Equality
open import Agda.Builtin.Nat
test : [ 1 , 2 , 3 , 4 ] ≡ _
test = refl
open import Agda.Builtin.Bool
isSingleton : List Nat → Bool
isSingleton ([ _ ]) = true
isSingleton _ = false
Once we get typed pattern synonyms (NB: they are no active efforts to
implement them) it should at least solve the issue that the pattern _,_
clashes with the constructor for sigma types.
In my own projects I tend to just write a ∷ b ∷ c ∷ ....

Related

How to inspect the value inside a product (Σ)?

I am trying to wrap my head around the Σ type by playing with a filterVec function, analogous to the filter function available for lists. See my implementation below:
-- Some imports we will need later
open import Data.Bool
open import Data.Nat
open import Data.Product
open import Data.Vec
-- The filterVec function
filterVec : {n : ℕ} -> (ℕ -> Bool) -> Vec ℕ n -> Σ ℕ (λ length → Vec ℕ length)
filterVec _ [] = 0 , []
filterVec f (x ∷ xs) with filterVec f xs
... | length , filtered = if f x then (suc length , x ∷ filtered) else (length , filtered)
Happy with my function, I decided to test it
dummyVec : Vec ℕ 5
dummyVec = 1 ∷ 2 ∷ 3 ∷ 4 ∷ 5 ∷ []
dummyFn : Vec ℕ 5
dummyFn with filterVec (λ _ → true) dummyVec
dummyFn | length , xs = {!!} -- I would like to return xs here, but Agda throws a type error
For some reason, Agda is unable to tell that length is 5, so it doesn't allow me to return xs. However, the code below does work:
open import Relation.Binary.PropositionalEquality
dummyProof : proj₁ (filterVec (λ _ → true) dummyVec) ≡ 5
dummyProof = refl
I am utterly confused by this behavior. In the case of dummyFn, Agda cannot tell that the length is 5, but in the case of dummyProof, Agda proves it without any problem.
What am I missing? How can I get dummyFn to work?
with binds a value to a name. If you want to compute, you can use let:
dummyFn′ : Vec ℕ 5
dummyFn′ = let length , xs = filterVec (λ _ → true) dummyVec
in xs
Let's look at the following example:
dummy : Set
dummy with 5
dummy | x = {!!}
Should the x magically reduce to 5 in the hole? No. You've given 5 another name — x, and those expressions are neither judgementally nor propositionally equal.
Now in your example:
dummyFn : Vec ℕ 5
dummyFn with filterVec (λ _ → true) dummyVec
dummyFn | length , xs = {!!}
You've given the length name to the length of xs. And it does not reduce to 5. It's a name. If you want both to give a name and to remember what the name is for, there is the inspect idiom for this:
dummyFn′′ : Vec ℕ 5
dummyFn′′ with filterVec (λ _ → true) dummyVec | inspect (filterVec (λ _ → true)) dummyVec
dummyFn′′ | length , xs | [ refl ] = xs

nicer way to write constructors as function in Agda

I have a list
data List (X : Set) : Set where
<> : List X
_,_ : X -> List X -> List X
a definition for equality
data _==_ {l}{X : Set l}(x : X) : X -> Set l where
refl : x == x
and congruence
cong : forall {k l}{X : Set k}{Y : Set l}(f : X -> Y){x y} -> x == y -> f x == f y
cong f refl = refl
I am trying to prove
propFlatten2 : {X : Set } ( xs0 : List X ) (x : X) (xs1 : List X) (xs2 : List X)
-> ( xs0 ++ x , xs1 ) ++ xs2 == xs0 ++ (x , xs1 ++ xs2 )
propFlatten2 <> x xs1 xs2 = refl
propFlatten2 (x , xs0) x₁ xs1 xs2 = cong (λ l -> x , l) {!!}
Is there a better way to use directly the constructor _,_ other than through a lambda in the last line ?
Agda doesn't have any special syntax for partial application of operators. You can, however, use the operators in their usual prefix version:
x + y = _+_ x y
This is convenient when you need to partially apply leftmost argument(s):
_+_ 1 = λ x → 1 + x
When you need to partially apply arguments going from the right, your options are more limited. As mentioned in the comments, you could use one of the convenience functions such as flip (found in Function):
flip f x y = f y x -- Type omitted for brevity.
And then simply flip the arguments of _+_:
flip _+_ 1 = λ x → x + 1
Sometimes you find operators whose only purpose is to make the code a bit nicer. Best example I can think of is probably Data.Product.,_. When you write a dependent pair (Data.Product.Σ), sometimes the first part of the pair can be filled in automatically. Instead of writing:
_ , x
You can just write:
, x
It's hard to say when writing a specialized operator such as the one above is actually worth it; if your only use case is using it with congruence, I'd just stick with the lambda since it makes it very clear what's going on.

Merge sort in agda

I want to implement merge sort in agda. If I do this in a naive way, the termination checker fails to pass the program because after we split input list into two parts, and then call ourselves recursively, agda doesn't know that size of each of the lists is smaller than the size of the original list.
I've seen several solutions, for example this one: https://gist.github.com/twanvl/5635740 but the code seems too complicated to me, and the worst thing is that we intermix the program and the proof.
There are at least three ways in which you can write merge sort so that it passes the termination checker.
First of all, here's what we need to make our merge sort generic:
open import Relation.Binary
open import Relation.Binary.PropositionalEquality
module MergeSort
{ℓ a} {A : Set a}
{_<_ : Rel A ℓ}
(strictTotalOrder : IsStrictTotalOrder _≡_ _<_) where
open IsStrictTotalOrder strictTotalOrder
Once we prove that some relation is a strict total order, we can use that proof as a parameter to this module and get corresponding merge sort.
The first way is to use well-founded recursion, which is more or less what the linked code in your question uses. However, we do not need to prove that merge sort returns a sorted permutation of its input list in a bounded number of comparisions, so we can cut most of the code.
I wrote something about well-founded recursion in this answer, you might want to check it out.
Other imports first:
open import Data.List
open import Data.Nat
hiding (compare)
open import Data.Product
open import Function
open import Induction.Nat
open import Induction.WellFounded
Here's the implementation of merge:
merge : (xs ys : List A) → List A
merge [] ys = ys
merge xs [] = xs
merge (x ∷ xs) (y ∷ ys) with compare x y
... | tri< _ _ _ = x ∷ merge xs (y ∷ ys)
... | tri≈ _ _ _ = x ∷ merge xs (y ∷ ys)
... | tri> _ _ _ = y ∷ merge (x ∷ xs) ys
If you are having problems getting this past termination checker, check out my answer on this. It should work as is with the development version of Agda.
split is easy as well:
split : List A → List A × List A
split [] = [] , []
split (x ∷ xs) with split xs
... | l , r = x ∷ r , l
But now we get to the complicated part. We need to show that split returns two lists that are both smaller than the original (which of course holds only if the original list had at least two elements). For this purpose, we define a new relation on lists: xs <ₗ ys holds iff length x < length y:
_<ₗ_ : Rel (List A) _
_<ₗ_ = _<′_ on length
The proof is then fairly straightforward, it's just an induction on the list:
-- Lemma.
s≤′s : ∀ {m n} → m ≤′ n → suc m ≤′ suc n
s≤′s ≤′-refl = ≤′-refl
s≤′s (≤′-step p) = ≤′-step (s≤′s p)
split-less : ∀ (x : A) y ys →
let xs = x ∷ y ∷ ys
l , r = split (x ∷ y ∷ ys)
in l <ₗ xs × r <ₗ xs
split-less _ _ [] = ≤′-refl , ≤′-refl
split-less _ _ (_ ∷ []) = ≤′-refl , ≤′-step ≤′-refl
split-less _ _ (x ∷ y ∷ ys) with split-less x y ys
... | p₁ , p₂ = ≤′-step (s≤′s p₁) , ≤′-step (s≤′s p₂)
Now we have everything we need in order to bring the well-founded recursion machinery. Standard library gives us proof that _<′_ is well-founded relation, we can use this to construct a proof that our freshly defined _<ₗ_ is also well-founded:
open Inverse-image {A = List A} {_<_ = _<′_} length
renaming (well-founded to <ₗ-well-founded)
open All (<ₗ-well-founded <-well-founded)
renaming (wfRec to <ₗ-rec)
And finally, we use <ₗ-rec to write merge-sort.
merge-sort : List A → List A
merge-sort = <ₗ-rec _ _ go
where
go : (xs : List A) → (∀ ys → ys <ₗ xs → List A) → List A
go [] rec = []
go (x ∷ []) rec = x ∷ []
go (x ∷ y ∷ ys) rec =
let (l , r) = split (x ∷ y ∷ ys)
(p₁ , p₂) = split-less x y ys
in merge (rec l p₁) (rec r p₂)
Notice that in the recursive call (rec), we not only specify what to recurse on, but also a proof that the argument is smaller than the original one.
The second way is to use sized types. I also wrote an overview in this answer, so you might want to check it out.
We need this pragma at the top of the file:
{-# OPTIONS --sized-types #-}
And a different set of imports:
open import Data.Product
open import Function
open import Size
However, we cannot reuse lists from standard library since they do not use sized types. Let's define our own version:
infixr 5 _∷_
data List {a} (A : Set a) : {ι : Size} → Set a where
[] : ∀ {ι} → List A {↑ ι}
_∷_ : ∀ {ι} → A → List A {ι} → List A {↑ ι}
merge stay more or less the same, we only need to change the type a bit to convince the termination checker:
merge : ∀ {ι} → List A {ι} → List A → List A
However, split has a slight but very important change:
split : ∀ {ι} → List A {ι} → List A {ι} × List A {ι}
split [] = [] , []
split (x ∷ xs) with split xs
... | l , r = x ∷ r , l
The implementation stays the same, but the type changed. What this change does is that it tells Agda that split is size-preserving. This means that the two resulting lists cannot be larger than the input one. merge-sort then looks very natural:
merge-sort : ∀ {ι} → List A {ι} → List A
merge-sort [] = []
merge-sort (x ∷ []) = x ∷ []
merge-sort (x ∷ y ∷ ys) =
let l , r = split ys
in merge (merge-sort (x ∷ l)) (merge-sort (y ∷ r))
And indeed, this gets past the termination checker. The trick is the above mentioned size-preserving property: Agda can see that split ys does not produce lists larger than ys and thus x ∷ l and y ∷ r are both smaller than x ∷ y ∷ ys. This is enough to convince the termination checker.
The last one is not really a merge sort in the usual sense. It uses the same idea but instead of repeatedly splitting the lists, recursively sorting them and then merging them together, it does all the splitting upfront, stores the results in a tree and then folds the tree using merge.
However, since this answer is already fairly long, I'll just give you a link.

Instance Implicits for Type Checking

I am learning how "typeclasses" are implemented in Agda. As an example, I am trying to implement Roman numerals whose composition with # would type-check.
I am not clear why Agda complains there is no instance for Join (Roman _ _) (Roman _ _) _ - clearly, it couldn't work out what natural numbers to substitute there.
Is there a nicer way to introduce Roman numbers that don't have "constructor" form? I have a constructor "madeup", which probably would need to be private, to be sure I have only "trusted" ways to construct other Roman numbers through Join.
module Romans where
data ℕ : Set where
zero : ℕ
succ : ℕ → ℕ
infixr 4 _+_ _*_ _#_
_+_ : ℕ → ℕ → ℕ
zero + x = x
succ y + x = succ (y + x)
_*_ : ℕ → ℕ → ℕ
zero * x = zero
succ y * x = x + (y * x)
one = succ zero
data Roman : ℕ → ℕ → Set where
i : Roman one one
{- v : Roman one five
x : Roman ten one
... -}
madeup : ∀ {a b} (x : Roman a b) → (c : ℕ) → Roman a c
record Join (A B C : Set) : Set where
field jo : A → B → C
two : ∀ {a} → Join (Roman a one) (Roman a one) (Roman a (one + one))
two = record { jo = λ l r → madeup l (one + one) }
_#_ : ∀ {a b c d C} → {{j : Join (Roman a b) (Roman c d) C}} → Roman a b → Roman c d → C
(_#_) {{j}} = Join.jo j
-- roman = (_#_) {{two}} i i -- works
roman : Roman one (one + one)
roman = {! i # i!} -- doesn't work
Clearly, if I specify the implicit explicitly, it works - so I am confident it is not the type of the function that is wrong.
Your example works fine in development version of Agda. If you are using a version older than 2.3.2, this passage from release notes could clarify why it doesn't compile for you:
* Instance arguments resolution will now consider candidates which
still expect hidden arguments. For example:
record Eq (A : Set) : Set where
field eq : A → A → Bool
open Eq {{...}}
eqFin : {n : ℕ} → Eq (Fin n)
eqFin = record { eq = primEqFin }
testFin : Bool
testFin = eq fin1 fin2
The type-checker will now resolve the instance argument of the eq
function to eqFin {_}. This is only done for hidden arguments, not
instance arguments, so that the instance search stays non-recursive.
(source)
That is, before 2.3.2, the instance search would completly ignore your two instance because it has a hidden argument.
While instance arguments behave a bit like type classes, note that they will only commit to an instance if there's only one type correct version in scope and they will not perform a recursive search:
Instance argument resolution is not recursive. As an example,
consider the following "parametrised instance":
eq-List : {A : Set} → Eq A → Eq (List A)
eq-List {A} eq = record { equal = eq-List-A }
where
eq-List-A : List A → List A → Bool
eq-List-A [] [] = true
eq-List-A (a ∷ as) (b ∷ bs) = equal a b ∧ eq-List-A as bs
eq-List-A _ _ = false
Assume that the only Eq instances in scope are eq-List and eq-ℕ.
Then the following code does not type-check:
test = equal (1 ∷ 2 ∷ []) (3 ∷ 4 ∷ [])
However, we can make the code work by constructing a suitable
instance manually:
test′ = equal (1 ∷ 2 ∷ []) (3 ∷ 4 ∷ [])
where eq-List-ℕ = eq-List eq-ℕ
By restricting the "instance search" to be non-recursive we avoid
introducing a new, compile-time-only evaluation model to Agda.
(source)
Now, as for the second part of the question: I'm not exactly sure what your final goal is, the structure of the code ultimately depends on what you want to do once you construct the number. That being said, I wrote down a small program that allows you to enter roman numerals without going through the explicit data type (forgive me if I didn't catch your intent clearly):
A roman numeral will be a function which takes a pair of natural numbers - the value of previous numeral and the running total. If it's smaller than previous numeral, we'll subtract its value from the running total, otherwise we add it up. We return the new running total and value of current numeral.
Of course, this is far from perfect, because there's nothing to prevent us from typing I I X and we end up evaluating this as 10. I leave this as an exercise for the interested reader. :)
Imports first (note that I'm using the standard library here, if you do not want to install it, you can just copy the definition from the online repo):
open import Data.Bool
open import Data.Nat
open import Data.Product
open import Relation.Binary
open import Relation.Nullary.Decidable
This is our numeral factory:
_<?_ : Decidable _<_
m <? n = suc m ≤? n
makeNumeral : ℕ → ℕ × ℕ → ℕ × ℕ
makeNumeral n (p , c) with ⌊ n <? p ⌋
... | true = n , c ∸ n
... | false = n , c + n
And we can make a few numerals:
infix 500 I_ V_ X_
I_ = makeNumeral 1
V_ = makeNumeral 5
X_ = makeNumeral 10
Next, we have to apply this chain of functions to something and then extract the running total. This is not the greatest solution, but it looks nice in code:
⟧ : ℕ × ℕ
⟧ = 0 , 0
infix 400 ⟦_
⟦_ : ℕ × ℕ → ℕ
⟦ (_ , c) = c
And finally:
test₁ : ℕ
test₁ = ⟦ X I X ⟧
test₂ : ℕ
test₂ = ⟦ X I V ⟧
Evaluating test₁ via C-c C-n gives us 19, test₂ then 14.
Of course, you can move these invariants into the data type, add new invariants and so on.

Agda: parse a string with numbers

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 type Maybe (List ℕ)
(optional, not important) How can I convert the type Maybe (List (Maybe ℕ)) to Maybe (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 split function into something more general which also works with the termination check:
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ℕ "" should most likely be nothing, unless you really want:
stringListToℕ "1,,2" ≡ just (1 ∷ 0 ∷ 2 ∷ [])
Let's rewrite it a bit (note that helper is your original stringToℕ function):
stringToℕ : List Char → Maybe ℕ
stringToℕ [] = nothing
stringToℕ list = helper list 0
where {- ... -}
And now we can put it all together. For simplicity I'm using List Char everywhere, sprinkle with fromList/toList as necessary):
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
You can find sequence in Data.List; we also have to specify which monad instance we want to use. Data.Maybe exports its monad instance under the name monad. Final code:
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
And a small test:
open import Relation.Binary.PropositionalEquality
test : stringListToℕ ('1' ∷ '2' ∷ ',' ∷ '3' ∷ []) ≡ just (12 ∷ 3 ∷ [])
test = refl
Considering your second question: there are many ways to turn a Maybe (List (Maybe ℕ)) into a Maybe (List ℕ), for example:
silly : Maybe (List (Maybe ℕ)) → Maybe (List ℕ)
silly _ = nothing
Right, this doesn't do much. We'd like the conversion to preserve the elements if they are all just. isNothing already does this part of checking but it cannot get rid of the inner Maybe layer.
from-just could work since we know that when we use it, all elements of the List must be just x for some x. The problem is that conv in its current form is just wrong - from-just works as a function of type Maybe A → A only when the Maybe value is just x! We could very well do something like this:
test₂ : Maybe (List ℕ)
test₂ = conv ∘ just $ nothing ∷ just 1 ∷ []
And since from-list behaves as a Maybe A → ⊤ when given nothing, 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 are just x and in this case put them all into a list wrapped in a just, otherwise return nothing
squash the doubled Maybe layer into one
Well, the second point sounds familiar - that's something monads can do! We get:
join : {A : Set} → Maybe (Maybe A) → Maybe A
join mm = mm >>= λ x → x
where
open RawMonad Data.Maybe.monad
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 a Maybe (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 (Traversable from Data.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 for Lists and we're back at sequence again.
There's still one piece missing, let's look at what we have so far:
sequence-maybe : List (Maybe ℕ) → Maybe (List ℕ)
sequence-maybe = sequence Data.Maybe.monad
join : Maybe (Maybe (List ℕ)) → Maybe (List ℕ)
-- substituting A with List ℕ
We need to apply sequence-maybe inside one Maybe layer. That's where the Maybe functor 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 type a → b into a function of type Maybe a → Maybe b. And finally:
open import Category.Functor
open import Data.Maybe
final : Maybe (List (Maybe ℕ)) → Maybe (List ℕ)
final mlm = join (sequence-maybe <$> mlm)
where
open RawFunctor functor
I had a go at it trying not to be clever and using simple recursive functions rather than stdlib magic. parse xs m ns parses xs by recording the (possibly empty) prefix already read in m while keeping the list of numbers already parsed in the accumulator ns.
If a parsing failure happens (non recognized character, two consecutive ,, etc.) everything is thrown away and we return nothing.
module parseList where
open import Data.Nat
open import Data.List
open import Data.Maybe
open import Data.Char
open import Data.String
isDigit : Char → Maybe ℕ
isDigit '0' = just 0
isDigit '1' = just 1
isDigit '2' = just 2
isDigit '3' = just 3
isDigit _ = nothing
attach : Maybe ℕ → ℕ → ℕ
attach nothing n = n
attach (just m) n = 10 * m + n
Quote : List Char → Maybe (List ℕ)
Quote xs = parse xs nothing []
where
parse : List Char → Maybe ℕ → List ℕ → Maybe (List ℕ)
parse [] nothing ns = just ns
parse [] (just n) ns = just (n ∷ ns)
parse (',' ∷ tl) (just n) ns = parse tl nothing (n ∷ ns)
parse (hd ∷ tl) m ns with isDigit hd
... | nothing = nothing
... | just n = parse tl (just (attach m n)) ns
stringListToℕ : String → Maybe (List ℕ)
stringListToℕ xs with Quote (toList xs)
... | nothing = nothing
... | just ns = just (reverse ns)
open import Relation.Binary.PropositionalEquality
test : stringListToℕ ("12,3") ≡ just (12 ∷ 3 ∷ [])
test = refl
Here is the Code from Vitus as a running example that uses the Agda Prelude
module Parse where
open import Prelude
-- Install Prelude
---- clone this git repo:
---- https://github.com/fkettelhoit/agda-prelude
-- Configure Prelude
--- press Meta/Alt and the letter X together
--- type "customize-group" (i.e. in the mini buffer)
--- type "agda2"
--- expand the Entry "Agda2 Include Dirs:"
--- add the directory
open import Data.Product using (uncurry′)
open import Data.Maybe using ()
open import Data.List using (sequence)
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
charsToℕ : List Char → Maybe ℕ
charsToℕ [] = nothing
charsToℕ list = stringToℕ (fromList list)
notComma : Char → Bool
notComma c = not (c == ',')
-- Finally:
charListToℕ : List Char → Maybe (List ℕ)
charListToℕ = Data.List.sequence Data.Maybe.monad ∘ map charsToℕ ∘ splitBy notComma
stringListToℕ : String → Maybe (List ℕ)
stringListToℕ = charListToℕ ∘ toList
-- Test
test1 : charListToℕ ('1' ∷ '2' ∷ ',' ∷ '3' ∷ []) ≡ just (12 ∷ 3 ∷ [])
test1 = refl
test2 : stringListToℕ "12,33" ≡ just (12 ∷ 33 ∷ [])
test2 = refl
test3 : stringListToℕ ",,," ≡ nothing
test3 = refl
test4 : stringListToℕ "abc,def" ≡ nothing
test4 = refl

Resources