Your post is very large and contains numerous elements, which all can be commented in different ways. I will address them one by one, and explain the choices I made to make these elements more easily accessible. Note that these choices consist in minor elements in your codes, mostly cosmetic, which do not change in any way the semantics of your definitions. I start by giving you the correct code in details, after which I answer the questions.
The correct code in details
Let us start by cleaning up these imports to the minimum needed:
module FSA whereopen import Data.List.Baseopen import Data.Emptyopen import Data.Unit
I then kept your definition of your automaton record:
record FSA : Set₁ where field Q : SetΣ : Setδ : Q →Σ→ Q q₀ : Q F : Q → Set
I have extracted your helper
function from the evalFSA'
function. The reason for this change is that when using when
, the function inherits all the parameters from the parent function, which makes it harder to comprehend further goals such as modal.helper M 0' (x1 ∷ xs) M xs (δ' S₁ x1)
.
helper : (fsa : FSA) → List (FSA.Σ fsa) → (FSA.Q fsa) → Sethelper fsa [] q = FSA.F fsa qhelper fsa (x ∷ xs) q = helper fsa xs ((FSA.δ fsa) q x)evalFSA' : (fsa : FSA) → List (FSA.Σ fsa) → SetevalFSA' fsa [] = ⊥evalFSA' fsa (x ∷ xs) = helper fsa (x ∷ xs) (FSA.q₀ fsa)
Then your case study automaton remains the same, although I simplified the record instantiation without using copatterns:
data Q' : Set where S₁ : Q' S₂ : Q'data Σ' : Set where 0' : Σ' 1' : Σ'q₀' : Q'q₀' = S₁F' : Q'→ SetF' S₁ = ⊤F' S₂ = ⊥δ' : Q'→Σ'→ Q'δ' S₁ 0' = S₁δ' S₁ 1' = S₂δ' S₂ 0' = S₁δ' S₂ 1' = S₂M : FSAM = record { Q = Q' ; Σ = Σ' ; δ = δ' ; q₀ = q₀' ; F = F' }
I also simplified your predicate endsWith0
as follows:
endsWith0 : List Σ'→ SetendsWith0 [] = ⊥endsWith0 (0'∷ []) = ⊤endsWith0 (_ ∷ xs) = endsWith0 xs
From this point on, soundM
and completeM
are proved as follows (I homogenized their signatures):
soundM : ∀ xs → evalFSA' M xs → endsWith0 xssoundM (0'∷ []) evM = evMsoundM (0'∷ x₁∷ xs) evM = soundM (x₁∷ xs) evMsoundM (1'∷ 0'∷ xs) evM = soundM (0'∷ xs) evMsoundM (1'∷ 1'∷ xs) evM = soundM (1'∷ xs) evMcompleteM : ∀ xs → endsWith0 xs → evalFSA' M xscompleteM (0'∷ []) ex = excompleteM (0'∷ x₁∷ xs) = completeM (x₁∷ xs)completeM (1'∷ 0'∷ xs) = completeM (0'∷ xs)completeM (1'∷ 1'∷ xs) = completeM (1'∷ xs)
Answers to non-proof related questions
- On predicates vs functions returning types, you asked:
I didn't implement evalFSA as a predicate, but rather as function, and am confused as to whether this was the right or wrong choice
There are no good answer to this question. Both ideas are possible, and often debated on other questions on this site. I personally always use predicates when possible, but others have arguments in favor of functions returning ⊤
or ⊥
. And, as you noticed it is possible to prove what you wanted using your implementation.
- On the weird highlighting, you asked:
it highlights the below line blue. what does this mean
As far as I know, this is a bug. Some weird coloration like that started to occasionally happen to me recently as well, but they apparently do not mean anything.
Answer to your proof-related question
You asked the following question:
why can't I apply the recursive call in the 1' case? the only difference between the f's is the use current state of the machine and the input string, but obviously this seems like a symmetry of the system that shouldn't effect our ability to compute
Answering this question is actually quite simple, but this answer was somewhat hidden in your implementation because you embedded the definition of helper
inside the definition of evalFSA'
, which I changed as explained.
Let us consider the following code while proving soundM
:
soundM : (xs : List Σ') → evalFSA' M xs → endsWith0 xssoundM (0'∷ []) evM = evMsoundM (0'∷ x ∷ xs) evM = soundM (x ∷ xs) {!evM!}soundM (1'∷ x ∷ xs) evM = soundM (x ∷ xs) {!evM!}
When asking Agda what is the goal and the type of the current element in the first goal, it answers:
Goal: helper M xs (δ' q₀' x)Have: helper M xs (δ' S₁ x)
Since you have defined q₀'
as follows:
q₀' : Q'q₀' = S₁
Agda know that q₀'
is definitionally equal to S₁
which means the current term has actually the same type as the goal, which is why it is accepted.
In the other hole however, asking Agda the same information gives:
Goal: helper M xs (δ' q₀' x)Have: helper M xs (δ' S₂ x)
In this case, q₀'
is not definitionally equal to S₂
(and not equal in any way actually) which means these types are not equal, and it is not possible to conclude right away.
As shown in my code, pattern matching an additional time on x
allows agda to further reduce the goal which allows us to conclude.
A similar reasoning is used to provide a proof of completeM