-
Notifications
You must be signed in to change notification settings - Fork 2
/
FreeMonadicProving.v
2327 lines (1854 loc) · 108 KB
/
FreeMonadicProving.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(** printing 0 %$\coqdocvar{O}$% *)
(** printing >>= %\ensuremath{>\kern-0.4ex>\kern-0.2ex=}% *)
(** printing (===>) %\ensuremath{(\Longrightarrow)}% *)
(** printing ===> %\ensuremath{\Longrightarrow}% *)
(** printing fun %\ensuremath{\lambda }% *)
(** printing - %\ensuremath{-}% *)
(** printing + %\ensuremath{+}% *)
(** printing tau %$\tau$% *)
(** printing tau' %$\tau'$% *)
(** printing S__Zero %$\coqdocvar{Shape}_\coqdocvar{Zero}$% *)
(** printing P__Zero %$\coqdocvar{Pos}_\coqdocvar{Zero}$% *)
(** printing S__One %$\coqdocvar{Shape}_\coqdocvar{One}$% *)
(** printing P__One %$\coqdocvar{Pos}_\coqdocvar{One}$% *)
(** printing E__One %$\coqdocvar{Ext}_\coqdocvar{One}$% *)
(** printing to__One %$\coqdocvar{to}_\coqdocvar{One}$% *)
(** printing from__One %$\coqdocvar{from}_\coqdocvar{One}$% *)
(** printing to_from__One %$\coqdocvar{to}\_\coqdocvar{from}_\coqdocvar{One}$% *)
(** printing from_to__One %$\coqdocvar{from}\_\coqdocvar{to}_\coqdocvar{One}$% *)
(** printing append'_assoc__Id %$\coqdocvar{append'\_assoc}_\coqdocvar{Id}$% *)
(** printing append_assoc__Id %$\coqdocvar{append\_assoc}_\coqdocvar{Id}$% *)
(** printing append'_assoc__Maybe %$\coqdocvar{append'\_assoc}_\coqdocvar{Maybe}$% *)
(** printing append_assoc__Maybe %$\coqdocvar{append\_assoc}_\coqdocvar{Maybe}$% *)
(** printing F__F %$\coqdocvar{F}_\coqdocvar{F}$% *)
(** printing M__M %$\coqdocvar{M}_\coqdocvar{M}$% *)
(** printing C__Zero %$\coqdocvar{C}_\coqdocvar{Zero}$% *)
(** printing C__One %$\coqdocvar{C}_\coqdocvar{One}$% *)
(** printing C__F %$\coqdocvar{C}_\coqdocvar{F}$% *)
(** printing to__Tree %$\coqdocvar{to}_\coqdocvar{Tree}$% *)
(** printing C__ND %$\coqdocvar{C}_\coqdocvar{ND}$% *)
(** printing C__simple %$\coqdocvar{C}_\coqdocvar{simple}$% *)
(** printing S__ND %$\coqdocvar{Shape}_\coqdocvar{ND}$% *)
(** printing P__ND %$\coqdocvar{Pos}_\coqdocvar{ND}$% *)
(** printing tau__1 %$\tau_\coqdocvar{1}$% *)
(** printing tau__n %$\tau_\coqdocvar{n}$% *)
(** printing tau__i %$\tau_\coqdocvar{i}$% *)
(** %\newpage\paragraph{Prologue}% This is a literate Coq file%\footnote{It can be downloaded at \url{https://zenodo.org/badge/latestdoi/136580106}.}%, that is,
it can be compiled with Coq%\footnote{The file was tested with Coq version 8.7 and 8.8.}% as it is. *)
(* begin hide *)
Require Import Coq.Logic.FunctionalExtensionality.
Require Import Coq.Program.Equality.
(* end hide *)
(**
* Preamble
%\label{section:introduction}%
%\lettrine{O}{nce}% upon a time there was a computer scientist called Mona D. living in the beautiful land of functional programming.
The land of functional programming was divided into several kingdoms.
Mona lived in the kingdom of Haskell, a land where all citizens used the pure lazy functional programming language Haskell.
Mona was pursuing her PhD when one day, her adviser told her about the magical kingdom of Coq.
In the magical kingdom of Coq, all citizens used the dependently typed programming language Coq.
With Coq, the citizens were not only able to write functional programs but also to formally prove properties about these programs.
Mona was fascinated by the idea of proving properties about programs.
While her colleagues in the department of software engineering were still writing tests and hoped to catch all bugs that way, she could once and for all prove that her programs were correct.
Mona's adviser suggested to her to visit an allied department where she could stay for a semester.
With her good intentions at heart, our hero set out to the kingdom of Coq to learn more about the Coq programming language.
Soon after Mona got to the kingdom of Coq she realized that proving statements about programs came at a price in Coq.
In Coq, all functions have to be provably total.
Most of the citizens in the kingdom of Haskell, however, did not care about totality as they were used to working with partial functions.
Therefore, Mona could not simply use the Haskell programs she had one-to-one in Coq.
Our hero went to the largest library in the kingdom of Coq and consulted many %manuscripts\footnote{In Mona's days the term manuscript was used as a synonym for all kinds of scientific references.}%
written by sages from the entire land of functional programming.
The first manuscript she found was written by %\cite{breitner2018ready}%.
In this manuscript, the authors proved statements about Haskell programs in Coq.
They transformed Haskell programs into Coq programs by keeping as much of the structure of the Haskell programs as possible.
In order to model partial functions they added an opaque constant [default : A] for every non-empty type [A].
By means of this constant they defined a function [error : String -> A] that was used to model the corresponding Haskell function.
Because [default] is opaque, the concrete value of [default] cannot be used in any proofs.
While Mona really liked the manuscript by %\citeauthor*{breitner2018ready}%, they could not reason about partial values and, thus, about the strictness of an implementation.
Furthermore, she found manuscripts that emphasized the importance of considering partial values.
For example, in a manuscript by %\cite{johann2004free}% she read about the [destroy]/[unfoldr] rule as presented by %\cite{svenningsson2002shortcut}%.
This rule fuses a list consumer with a list producer.
The [destroy]/[unfoldr] rule does not hold if partial values are considered and a strict evaluation primitive like %\texttt{seq}% is available.
Moreover, partial functions are often necessary in the implementation of data structures, whose invariants cannot be encoded in Haskell's type system.
For example, a function might be undefined for an empty list but an invariant states that the argument will never be an empty list.
Mona also read a manuscript by %\cite{danielsson2006fast}% about relating the total interpretation of a function with an interpretation of the same function that models partiality.
In essence, when the function is only applied to total arguments, the two interpretations behave the same.
While Mona saw the importance of this connection, the approach was still not able to argue about partial functions.
For example, it was not sufficient that a program transformation held for all total arguments, if the compiler could not enforce that the arguments are always total.
Mona did not give up.
There had to be a way to get the best of both worlds.
That is, argue about a Haskell function with respect to total values whenever possible and argue about partial values only if necessary.
Mona went to the largest library in the whole land of functional programming.
And indeed she found a manuscript by %\cite{abel2005verifying}% that discussed exactly the problem she was trying to solve.
%\citeauthor*{abel2005verifying}% lived in the nearby magical kingdom of Agda, where dependently typed programming was also highly regarded by its citizens.
%\citeauthor*{abel2005verifying}% presented an approach to translate Haskell programs into monadic Agda programs.
By instantiating the monadic program with a concrete monad instance one could choose the model a statement should be proven in.
For example, if Mona chose the identity monad, she would work in a total world while the maybe monad allowed her to argue about partiality.
Furthermore, she would not have to make any changes to her program but could argue about the same program regardless of the monad she chose.
%\citeauthor*{abel2005verifying}% used monadic expressions to model possibly undefined expressions.
As Haskell is non-strict, a data structure may contain an undefined expression that is never demanded.
For example, a cons may have an undefined tail.
Mona enthusiastically defined the following inductive data type for lists in Coq, which resembled the corresponding definition by %\citeauthor*{abel2005verifying}% in Agda.%\footnote{Coq uses GADT-style data type definitions.}
*)
Fail Inductive List M A :=
| nil : List M A
| cons : M A -> M (List M A) -> List M A.
(**
In Coq, all arguments of a type or function can be annotated: values as well as types.
As a Haskell programmer, Mona was not used to annotating type variables with their types, so she used the code convention that type variables would not be annotated, if they could be inferred --- just like she was used to in her Haskell programs.
That is, Mona could use [M : Type -> Type] and [A : Type] in the definition of [List], but these annotations were inferred by the system, so she left them out.
Moreover, she used the convention that constructors start with a lower-case letter.
When Mona tried to compile the definition of [List] in a current version of Coq, she was quite disappointed.
As indicated by the keyword [Fail], the Coq compiler accepted the definition [List] only if it caused an error.
In Mona's case, the Coq compiler presented the following error message.
%\begin{quote}
The command has indeed failed with message:\\
Non-strictly positive occurrence of \enquote{\coqdocvar{List}} in \enquote{\coqdocvar{M A} $\rightarrow$ \coqdocvar{M (List M A)} $\rightarrow$ \coqdocvar{List M A}}.
\end{quote}%
Mona thought that she had made a mistake when translating the Agda definition to Coq.
She wrote a letter to a friend in the kingdom of Agda.
A few days later she received an unexpected answer: the definition did not work in Agda either.
At the time the manuscript by %\cite{abel2005verifying}% was written, the rules in the kingdom of Agda were less strict, but that was a long time ago.
Meanwhile, the king of the kingdom of Agda had realized that the rules needed to be changed.
The programs that could be defined using the old rules were not safe!
Following the example of the magical kingdom of Coq, non-strictly positive occurrences as mentioned in the error message above were now disallowed in the kingdom of Agda as well.
In this story, Mona D. went on a journey to find a model for Haskell programs in Coq that obeyed the king's rules.
She learned about the laws of the kingdom of Coq from the perspective of a programmer from the kingdom of Haskell.
Mona would also meet fellows with an interest in more theoretical aspects of functional programs and approaches for generic programming in Haskell.
On the way to achieving her goal, there were many obstacles to overcome.
Led by her curiosity and her perseverance, reading a lot of manuscripts and meeting with her fellows would ultimately give Mona the inspiration how to tackle her problem.
* The Problem
%\label{section:the-problem}%
%\lettrine{M}{ona}% spent the next days searching for manuscripts about possible adaptations of the approach by %\cite{abel2005verifying}% that worked in a current version of Coq.
Nothing came up, but Mona did not want to give up.
Thus, she used her most powerful weapon in defeating scientific obstacles: her perseverance.
She tried multiple simplified versions of data type definitions similar to [List] and checked for each whether the error still occurred.
The simplest data type definition she found that caused the same error looked as follows.
*)
Fail Inductive NonStrictlyPos := con : (NonStrictlyPos -> nat) -> NonStrictlyPos.
(**
%\noindent% When Mona compiled this definition, she got the following error message.
%\begin{quote}
The command has indeed failed with message:\\
Non-strictly positive occurrence of \enquote{\coqdocvar{NonStrictlyPos}} in \enquote{\coqdocvar{(NonStrictlyPos} $\rightarrow$ nat) $\rightarrow$ \coqdocvar{NonStrictlyPos}}.
\end{quote}%
So Mona wondered what it meant that [NonStrictlyPos] occurs in a non-strictly positive way
in the type signature of [con : (NonStrictlyPos -> nat) -> NonStrictlyPos].
Mona remembered hearing about a class of Adam Chlipala explaining the intricacies of Coq very well.
She looked up the term %\enquote{strictly positive}% and indeed found the relevant note in %\citeauthor*{chlipala2011certified}%'s manuscript %\autocite{chlipala2011certified}%.
%\begin{quote}
We have run afoul of the strict positivity requirement for inductive definitions, which says that the type being defined may not occur to the left of an arrow in the type of a constructor argument.
\end{quote}%
An inductive type like [NonStrictlyPos] obeys the strict positivity requirement, if its recursive occurrences are strictly positive in the types of all arguments of its constructors.
Mona realized that the important part of the definition was that she needed to check the types of all arguments of a constructor and not the type of the constructor itself.
Here, the inductive type has only one constructor [con] and this constructor has only one argument of type [NonStrictlyPos -> nat].
A type [tau] occurs strictly positively in a type [tau__1 -> ... -> tau__n -> tau] if and only if [tau] does not occur in any of the types [tau__i] with %$i$% ranging from %$1$% to %$n$%.
Mona took another look at the malicious definition and understood.
The type [NonStrictlyPos] occurs non-strictly positively in one of its argument types, namely in [NonStrictlyPos -> nat].
The recursive occurrence of the type [NonStrictlyPos] is %\enquote{left of an arrow}%.
In other words [NonStrictlyPos] occurs as an argument type of [NonStrictlyPos -> nat], which makes [NonStrictlyPos] a non-strictly positive type.
In contrast, the following definition is not problematic.
*)
Inductive StrictlyPos := con : StrictlyPos -> (nat -> StrictlyPos) -> StrictlyPos.
(**
The first argument of [con] of type [StrictlyPos] is not problematic as it is trivially strictly positive.
In the second argument of [con], [StrictlyPos] does not occur to the left of an arrow in the type [nat -> StrictlyPos].
That is, as [StrictlyPos] occurs strictly positively in all arguments of the constructor [con], it fulfills the strict positivity requirement.
Mona still wondered why the king disallowed a definition like [NonStrictlyPos] in Coq.
At lunch the next day, she talked about her insights with some PhD students.
One of them had read about restrictions of dependently typed languages and explained to her that while the definition itself did not cause any trouble, it is possible to define functions that cause trouble.
Back at Mona's computer, they tried to understand the restriction given the following innocent looking function in Coq assuming that the above definition of [NonStrictlyPos] was accepted.
%\small%
[[
Definition applyFun (t : NonStrictlyPos) : nat :=
match t with
| con f => f t
end.
]]
%\normalsize%
The function [applyFun] simply took a value of type [NonStrictlyPos] and applied the function inside the argument of [con] to the value itself.
A problematic example usage of this function is the expression [applyFun (con applyFun)].
Reducing the expression by using the definition of [applyFun] yields [applyFun (con applyFun)] again, which indicates that this expression will not terminate.
Mona now remembered something she learned in her first course about functional programming in Haskell, namely the following data type.
*)
Fail Inductive Mu A := mu : (Mu A -> A) -> Mu A.
(**
%\noindent% This data type can be used to implement a fix-point combinator without explicit recursion.
Before her colleague left to prepare his next tutoring class, he suggested her to consult a manuscript by %\cite{mcadam1997wraps}% to refresh her memory about the [Mu] data type.
If Coq allows a data type like [Mu], all of a sudden general recursion is available and the corresponding logic becomes inconsistent.
Thus, a data type definition that has recursive occurrences to the left of an arrow as in [Mu] and [NonStrictlyPos] is not allowed.
When %\citeauthor*{abel2005verifying}% published their work, Agda did not perform any termination checks and, as a consequence, did not check the strict positivity requirement.
While Mona now understood why the data type as defined by %\citeauthor*{abel2005verifying}% is not allowed, it was still not obvious to her why the restriction is violated in the case of [List].
For example, if she explicitly used the maybe monad for [M], everything worked fine.
She thought about all the different monad instances she learned in class.
Then she remembered the continuation monad and realized that an arbitrary type constructor [M] might violate the restriction.
More concretely, she considered the following definition of the continuation data type.
*)
Definition Cont R A := (A -> R) -> R.
(**
%\noindent% When she instantiated [M] in the definition of [List] with [Cont R] for some [R], she got the following type that violated the strict positivity requirement.
*)
Fail Inductive ListC R A :=
| nilC : ListC R A
| consC : ((A -> R) -> R) -> ((ListC R A -> R) -> R) -> ListC R A.
(**
The definition of [consC] is more delicate than the definitions before.
Here, [ListC] occurs non-strictly positive in the second argument of the constructor [consC], namely [(ListC R A -> R) -> R].
Mona's colleague referred her to a manuscript by %\cite{coquand1990inductively}% that introduced the restriction to forbid non-strictly positive types in the kingdom of Coq.
He also brought to her attention that technically, although it was not the case, the type [ListC] could be allowed in Agda.
In Agda only types with recursive occurrences in negative positions have to be disallowed.
Negative positions are positions left of an odd number of arrows.
For example, the definition of [NonStrictlyPos] cannot be allowed because the recursive occurrence in [NonStrictlyPos -> nat] is left of one arrow.
Just as the definition of [Mu] cannot be allowed in Agda either because the recursive occurrence in [Mu A -> A] is left of one arrow.
In contrast the recursive occurrence in [(ListC R A -> R) -> R] is left of two arrows and, therefore, could be allowed in Agda.
Coq has to be more restrictive because the sort [Prop] is impredicative while it is predicative in Agda%~\autocite{coquand2013positive}%.
In summary, the type constructor [List] defined above allows arbitrary type constructors as instances of [M].
That is, it is not safe to use this definition for all potential instantiations of [M].
The restriction might be violated for a concrete instantiation, like for example [Cont] as used in [ListC].
Since it cannot be guaranteed by definition that this data type declaration is used safely, Coq rejects the declaration.
* The Solution
%\label{section:free-monads}%
%\lettrine{M}{ona}% realized that she could not use a type variable in the definition of the monadic list data type, and, thus, had to use a concrete data type.
However, she would still like to model several different monad instances in order to be able to reason about several different possible effects.
%\subsection*{Free Monads}%
%\label{subsection:free}%
Mona was a regular reader of the %\emph{Haskell Weekly News}%, a gazette where citizens of the kingdom of Haskell presented usages and developments of the Haskell programming language.
She remembered a lot of stories about monadic abstractions and in particular about the free monad.
The free monad turns any functor into a monad and is usually defined as a data type with two constructors, [pure] and [impure].
Mona could use the concrete data type definition of a free monad to represent the monadic part in the problematic list definition.
Keen to try out this idea, Mona defined the following data type, where the type variable [F] needs to be a functor to make [Free F] a monad.
*)
Fail Inductive Free F A :=
| pure : A -> Free F A
| impure : F (Free F A) -> Free F A.
(**
Again, Coq rejected the definition of this data type.
In the constructor [impure] the type variable [F] is applied to [Free F A] and, thus, is violating the aforementioned strict positivity requirement.
Mona was disappointed that she could not apply the idea to represent the monadic part of the list definition via the free monad to her problem.
As she was still a Haskell programmer with all her heart and the free monad was known to have various applications in functional programming, she continued to study free monads as a distraction from her current setback.
As Mona was mainly looking for a way to represent the identity and the maybe monad, she checked how these monads can be represented by instances of [Free].
She found the corresponding definitions in a manuscript that mentioned free monads, written by %\cite{swierstra2008data}%.
The identity monad corresponds to a free monad without the [impure] case, because the identity monad does not have any effect.
That is, in order to model the identity monad, Mona needed a functor that had no inhabitants.
This way, it was not possible to construct an impure value.
Mona defined the following Coq data type that has no inhabitants.
*)
Inductive Void := .
(**
%\noindent% Based on this definition Mona defined the following functor to model an instance of the free monad without impure values.
*)
Definition Zero (A : Type) := Void.
(**
%\noindent% Since the type variable [A] did not occur on the right-hand side of this definition, Mona had to actually annotate the sort in order to compile the definition in Coq.
In the case of the maybe monad there is an effect, namely the absence of a value, represented by the constructor [Nothing].
That is, Mona searched for a functor such that the [impure] constructor models the [Nothing] constructor.
Therefore, she needed a type constructor that has a single constructor but does not contain any value, such that the recursive occurrence of [Free F A] was not used.
She defined a functor with a single value by means of the data type [unit], which contains a single value called [tt].
*)
Definition One (A : Type) := unit.
(**
At first Mona was mainly interested in the identity and the maybe monad --- because %\cite{abel2005verifying}% also considered these effects to model Haskell programs.
However, when she checked out other definitions, she realized that using the free monad opens the door to model a variety of different monads.
For example, the error monad could be modeled by using the following functor.
*)
Inductive Const (B A : Type) := const : B -> Const B A.
(**
Mona had fun trying out various functors and checking what monad corresponded to the resulting instance of the free monad.
However, after a while the excitement vanished and Mona was kind of disappointed.
It felt as if she had not gained much by using a free monad.
Then, Mona suddenly had an insight.
By using a free monad she had gained one crucial advantage: she would be able to represent strictly positive monads if she was able to represent strictly positive functors.
Motivated by this insight, she gained new impulse.
She got in contact with a group of sages in the area of verification stating her problem concerning Coq's restriction.
These experts pointed her to a manuscript by %\cite{keuchel2013generic}%.
%\citeauthor*{keuchel2013generic}% wanted to define the following data type in Coq that can be used to define the fix point of a functor.
*)
Fail Inductive FixF F := fixF : F (FixF F) -> FixF F.
(**
The type [FixF] is a generalization of [Mu] as defined in %\autoref{section:the-problem}%.
Thus, the data type [FixF] cannot be defined in Coq because the functor [F] might place its argument [FixF F] in a non-strictly positive position.
In order to be able to define this data type the functor [F] has to be restricted.
This restriction is based on the notion of containers as introduced by %\cite{abott2003categories}%.
Mona was quite happy about the feedback because she could use the same approach to define [Free].
%\subsection*{Containers}%
%\label{subsection:containers}%
The idea of a container abstracts data types that store values.
A prominent example is the list data type.
A list can be represented by the length of the list and a function mapping positions within the list, that is, mapping natural numbers between one and the length of the list to the elements of the list.
This idea can be generalized to other data types like trees.
In this case, instead of a simple natural number, one needs a more complex data type to model all possible positions of a specific shape.
A container is given by a type [Shape] that models all possible shapes and a function [Pos] that takes a shape and yields the type of the possible positions of the given shape.
The extension of a container is the concrete data type that is modeled, that is, the extension provides a mapping of valid positions to values %\autocite{altenkirch2007generic}%.
Mona used the following implementation of a container extension in Coq.
*)
Inductive Ext Shape (Pos : Shape -> Type) A := ext : forall s, (Pos s -> A) -> Ext Shape Pos A.
(* begin hide *)
Arguments ext {_} {_} {_} s pf.
Set Implicit Arguments.
(* end hide *)
(**
%\noindent% For example, in the case of lists, the extension is the mapping of list indices to list elements.
Mona learned a lot about Coq by defining the data type [Ext].
For example, she learned that, in Coq --- in contrast to Haskell --- when a polymorphic constructor or function was applied, the type that was used as an instance was passed explicitly.
For example, the constructor [ext] was polymorphic over the type [A].
In order to apply [ext] Mona would have to pass the concrete instance for the type variable [A].
However, it was possible to persuade Coq to infer these types from the corresponding values.
Mona tweaked all constructors and functions in her Coq code so that they behaved like constructors and functions in Haskell.
For example, while [ext] would normally take five arguments in Coq --- the three types [Shape], [Pos] and [A], a shape [s : Shape] and a function of type [Pos s -> A], by tweaking it, it took only the last two arguments as a Haskell programmer would expect.
In order to understand the use of [Ext] Mona modeled the data type [One] as a container following the naming convention used by %\cite{swierstra2008data}%.
She came up with the following definition.
*)
Definition S__One := unit.
Definition P__One (s : S__One) := Void.
Definition E__One A := Ext S__One P__One A.
(**
The data type [One] was polymorphic over its type argument [A], which was used as a phantom type and,
therefore, not used on the right-hand side of the definition.
Furthermore, [One] had just one constructor that did not hold any value.
As [One] did not contain any values, the container only had a single shape without any positions.
Mona modeled the shape of [One] with the unit type.
She used the name [Shape] for the type of shapes, as in [S__One], and [Pos] for the corresponding position type.
The type of the extension of the container, which consists of the shape type [S__One] and the position function [P__One] as well as the type of the elements [A], was named [E__One].
The container extension [E__One] was isomorphic to [One].
In order to prove this property Mona defined two functions to transform these data types into one another.
She called these functions [to__One] and [from__One], respectively.
*)
Definition to__One A (e : E__One A) : One A := tt.
Definition from__One A (z : One A) : E__One A := ext tt (fun p : P__One tt => match p with end).
(**
The definition of [to__One] was straightforward as there was only a single value of type [One A].
The definition of [from__One] passed its unit value [tt] to the constructor [ext].
The second argument of [ext] was a function that could never be applied because its argument type has no inhabitants.
Therefore, the pattern matching on the position [p] could never be successful.
As the type of [p] had no inhabitants, the pattern matching did not have a right-hand side.
At last, Mona proved that [to__One] and [from__One] were actually inverse to each other and, thus, formed an isomorphism between [E__One] and [One].
*)
Lemma to_from__One : forall A (ox : One A), to__One (from__One ox) = ox.
(* begin hide *)
Proof.
intros A ox.
destruct ox.
unfold from__One.
unfold to__One.
reflexivity.
Qed.
(* end hide *)
Lemma from_to__One : forall A (e : E__One A), from__One (to__One e) = e.
(* begin hide *)
Proof.
intros A e.
destruct e.
destruct s.
unfold to__One.
unfold from__One.
apply f_equal.
extensionality p.
destruct p.
Qed.
(* end hide *)
(**
In order to define a data type that represented all free monads whose functor was a container, Mona defined a type class named [Container].
This type class was parametrized over a type constructor [F : Type -> Type] that was isomorphic to the corresponding container extension.
The type class provided the type of shapes and the mapping of a shape to the type of positions.
Furthermore, the type class provided functions [to] and [from] for transition between the functor and the corresponding container extension.
As Mona got more familiar with the advantages of Coq, the type class also provided propositions that [from] and [to] formed a bijection.
*)
Class Container F :=
{
Shape : Type;
Pos : Shape -> Type;
to : forall A, Ext Shape Pos A -> F A;
from : forall A, F A -> Ext Shape Pos A;
to_from : forall A (fx : F A), to (from fx) = fx;
from_to : forall A (e : Ext Shape Pos A), from (to e) = e
}.
(* begin hide *)
Arguments from {_} {_} {_} _.
(* end hide *)
(**
In order to complete the example for [One], Mona defined the following instance named [C__One]%\footnote{Mona could define multiple instances for the same type in Coq, because all instances were explicitly named.}%.
*)
(* begin hide *)
(*
We have to hide this definition because of a strange bug, see the comment at the
other definition
*)
Module Temp.
(* end hide *)
Instance C__One : Container One :=
{
Shape := S__One;
Pos := P__One;
to := to__One;
from := from__One;
to_from := to_from__One;
from_to := from_to__One
}.
(* begin hide *)
End Temp.
(* end hide *)
(* begin hide *)
Section Zero.
Definition S__Zero := Void.
Definition P__Zero (s : S__Zero) := Void.
Definition toZero A (e : Ext S__Zero P__Zero A) : Zero A :=
match e with
| ext s _ => match s with end
end.
Definition fromZero A (z : Zero A) : Ext S__Zero P__Zero A :=
match z with end.
Section ContainerZero.
Global Instance C__Zero : Container Zero :=
{
Shape := S__Zero;
Pos := P__Zero;
to := toZero;
from := fromZero
}.
Proof.
- intros.
destruct fx.
- intros.
destruct e.
destruct s.
Defined.
End ContainerZero.
End Zero.
(* end hide *)
(**
%\subsection*{Implementation}%
%\label{subsection:freeCoq}%
Equipped with these new insights about containers and their usage to define strictly positive types, Mona defined the following data type that implements a free monad whose functor is a container.
Here and in the following Mona assumed that [F : Type -> Type] was a type constructor.
*)
(* begin hide *)
Section Free.
Variable F : Type -> Type.
(* end hide *)
Inductive Free (C__F : Container F) A :=
| pure : A -> Free C__F A
| impure : Ext Shape Pos (Free C__F A) -> Free C__F A.
(* begin hide *)
End Free.
Arguments pure {_} {_} {_} _.
Section Free_Rect.
Variable F : Type -> Type.
Variable C__F : Container F.
Variable A : Type.
Variable P : Free C__F A -> Type.
Variable Pure_rect : forall (x : A), P (pure x).
Variable Impure_rect : forall (s : Shape) (pf : Pos s -> Free C__F A),
(forall p, P (pf p)) -> P (impure (ext s pf)).
Fixpoint Free_Rect (fx : Free C__F A) : P fx :=
match fx with
| pure x => Pure_rect x
| impure (ext s pf) =>
Impure_rect s pf (fun p : Pos s => Free_Rect (pf p))
end.
End Free_Rect.
Section Free_Ind.
Variable F : Type -> Type.
Variable C__F : Container F.
Variable A : Type.
Variable P : Free C__F A -> Prop.
Variable Pure_ind : forall (x : A), P (pure x).
Variable Impure_ind : forall (s : Shape) (pf : Pos s -> Free C__F A),
(forall p, P (pf p)) -> P (impure (ext s pf)).
Definition Free_Ind (fx : Free C__F A) : P fx := Free_Rect P Pure_ind Impure_ind fx.
End Free_Ind.
(* end hide *)
(* begin hide *)
Section MonadClass.
Class Monad (M: Type -> Type) :=
{
ret : forall A, A -> M A;
bind : forall A B, M A -> (A -> M B) -> M B;
left_unit : forall A B (x0: A) (f: A -> M B),
bind (ret x0) f = f x0;
right_unit : forall A (ma: M A), bind ma (@ret A) = ma;
bind_assoc: forall A B C (ma : M A) (f: A -> M B) (g: B -> M C),
bind ma (fun y => bind (f y) g) = bind (bind ma f) g
}.
Definition join (M: Type -> Type) `(Monad M) A (mmx : M (M A)) : M A := bind _ mmx (fun x => x).
End MonadClass.
Arguments join {_} {_} {_}.
Section MonadInstance.
Variable F : Type -> Type.
Variable C__F : Container F.
Definition cmap A B (f : A -> B) (x : Ext Shape Pos A) : Ext Shape Pos B :=
match x with
| ext s pf => ext s (fun x => f (pf x))
end.
Section fbind.
Variable A B: Type.
Variable f: A -> Free C__F B.
Fixpoint free_bind' (ffA: Free C__F A) :=
match ffA with
| pure x => f x
| impure e => impure (cmap free_bind' e)
end.
End fbind.
Definition free_bind A B (ffA: Free C__F A) (f: A -> Free C__F B) : Free C__F B :=
free_bind' f ffA.
Notation "mx >>= f" := (free_bind mx f) (at level 50, left associativity).
Notation "'do' x <- mx ; f" :=
(free_bind mx (fun x => f))
(at level 50, x ident, mx at level 40, f at level 50).
Lemma pure_bind :
forall A B (x: A) (f: A -> Free C__F B), pure x >>= f = f x.
Proof.
reflexivity.
Qed.
Lemma bind_pure :
forall A (fA: Free C__F A), fA >>= (fun x => pure x) = fA.
Proof.
induction fA using Free_Ind.
- reflexivity.
- simpl free_bind.
repeat apply f_equal.
apply functional_extensionality.
apply H.
Qed.
Lemma free_bind_assoc :
forall A B C (fa : Free C__F A) (f: A -> Free C__F B) (g: B -> Free C__F C),
fa >>= (fun y => f y >>= g) = fa >>= f >>= g.
Proof.
intros.
induction fa using Free_Ind.
- econstructor.
- simpl free_bind.
repeat apply f_equal.
apply functional_extensionality.
apply H.
Qed.
Global Instance free_monad : Monad (Free C__F) :=
{
ret := @pure F C__F;
bind := fun _ _ xs f => free_bind xs f;
left_unit := pure_bind;
right_unit := bind_pure;
bind_assoc := free_bind_assoc
}.
End MonadInstance.
Arguments cmap {_} {_} {_} {_}.
Notation "mx >>= f" := (free_bind mx f) (at level 50, left associativity).
Notation "'do' x <- mx ; f" :=
(free_bind mx (fun x => f))
(at level 50, x ident, mx at level 40, f at level 50).
(* end hide *)
(* begin hide *)
Section MonadInstances.
Definition Id (A : Type) := A.
Definition id_ret A (x : A) : Id A :=
x.
Definition id_bind A B (a : Id A) (f : A -> Id B) : Id B := f a.
Lemma id_left_identity :
forall A B (a : A) (f : A -> Id B),
id_bind (id_ret a) f = f a.
Proof.
repeat intro.
unfold id_ret.
unfold id_bind.
reflexivity.
Qed.
Lemma id_right_identity :
forall A (ia : Id A),
id_bind ia (fun a => id_ret a) = ia.
Proof.
repeat intro.
unfold id_bind.
unfold id_ret.
reflexivity.
Qed.
Lemma id_associativity :
forall A B C (ia : Id A) (f : A -> Id B) (g : B -> Id C),
id_bind ia (fun a => id_bind (f a) g) = id_bind (id_bind ia f) g.
Proof.
repeat intro.
unfold id_bind.
reflexivity.
Qed.
Global Instance id_monad : Monad Id :=
{
ret := id_ret;
bind := id_bind;
left_unit := id_left_identity;
right_unit := id_right_identity;
bind_assoc := id_associativity
}.
Inductive Maybe A :=
| nothing : Maybe A
| just : A -> Maybe A.
Global Arguments nothing {_}.
Definition maybe_ret A (a : A) : Maybe A :=
just a.
Definition maybe_bind A B (ma : Maybe A) (f : A -> Maybe B) : Maybe B :=
match ma with
| nothing => nothing
| just a => f a
end.
Lemma maybe_left_identity :
forall A B (a : A) (f : A -> Maybe B),
maybe_bind (maybe_ret a) f = f a.
Proof.
reflexivity.
Qed.
Lemma maybe_right_identity :
forall A (ma : Maybe A),
maybe_bind ma (@maybe_ret A) = ma.
Proof.
induction ma.
- reflexivity.
- reflexivity.
Qed.
Lemma maybe_associativity :
forall A B C (ma : Maybe A) (f : A -> Maybe B) (g : B -> Maybe C),
maybe_bind ma (fun a => maybe_bind (f a) g) = maybe_bind (maybe_bind ma f) g.
Proof.
intros.
induction ma.
- reflexivity.
- reflexivity.
Qed.
Global Instance maybe_monad : Monad Maybe :=
{
ret := maybe_ret;
bind := maybe_bind;
left_unit := maybe_left_identity;
right_unit := maybe_right_identity;
bind_assoc := maybe_associativity
}.
End MonadInstances.
(* end hide *)
(**
Instead of using an arbitrary functor [F] the definition of [Free] used a container extension to ensure that the definition was provably valid in Coq, i.e., [Free] only occurred in strictly positive positions.
To test her definition, Mona implemented the following abbreviation.
The value [Nothing]%\footnote{She used the convention that smart constructor names started with an upper case character.}% was the value of the free monad that represented the corresponding value of the maybe monad.
*)
(* begin hide *)
Instance C__One : Container One :=
{
Shape := S__One;
Pos := P__One;
to := to__One;
from := from__One;
to_from := to_from__One;
from_to := from_to__One
}.
(*
The definition of Nothing does not work if we define C__One at the other position.
*)
(*end hide *)
Definition Nothing A : Free C__One A := impure (ext tt (fun p : P__One tt => match p with end)).
(* begin hide *)
Arguments Nothing {_}.
Section FunctorClass.
Class Functor (F: Type -> Type) :=
{
fmap : forall A B, (A -> B) -> F A -> F B;
fmap_id : forall A (fx : F A), fmap (fun x => x) fx = fx;
fmap_comp : forall A B C (f : B -> C) (g : A -> B) (fx : F A),
fmap f (fmap g fx) = fmap (fun x => f (g x)) fx
}.
End FunctorClass.
Section Fold.
Variable F : Type -> Type.
Variable C__F : Container F.
Variable F__F : Functor F.
Fixpoint fold_free A B (pur : A -> B) (imp : F B -> B) (fx : Free C__F A) : B :=
match fx with
| pure x => pur x
| impure e => imp (to (cmap (fold_free pur imp) e))
end.
(* end hide *)
(**
When Mona continued reading about free monads, she read that from every natural transformation from a functor [f] to a monad [m] she can construct a monad homomorphism from [Free f] to [m].
Therefore, for every monad [m] there exists a monad homomorphism from an instance of [Free] to [m] because she can use the identity function as natural transformation.
More precisely, the simplest construction Mona can use to model a monad [m] is to represent it as [Free m].
The homomorphism was often defined by means of a fold function for the [Free] data type.
One other important function on [Free] that Mona wanted to define was the function bind, which was associated with monads.
She found the following definitions of [fold] and bind on [Free] in the manuscript by %\cite{swierstra2008data}%.
%\begin{verbatim}
foldFree :: Functor f => (a -> b) -> (f b -> b) -> Free f a -> b
foldFree pur imp (Pure x) = pur x
foldFree pur imp (Impure fx) = imp (fmap (foldFree pur imp) fx)
(>>=) :: Functor f => Free f a -> (a -> Free f b) -> Free f b
Pure x >>= f = f x
Impure fx >>= f = Impure (fmap (>>= f) fx)
\end{verbatim}%
Based on these definitions, Mona translated the functions to Coq, naming them [fold_free] and [>>=], respectively.
%\jump{If you are keen to know how Mona translated these functions to Coq, you and Mona can dive deeper into technical details in \autoref{app:foldbind}.
If you know how to work with recursive higher-order definitions and containers, or do not need any more details, just read ahead.}%
%\noindent% The function [fold_free] was only a means to an end.
Mona defined the function [induce], which lifts a function that maps the functor [F] to the corresponding monad [M]%\footnote{The monad \coqdocvar{M} is equipped with the functions \coqdocvar{ret} and \coqdocvar{join}.}% to a homomorphism between the free monad and the corresponding monad%\footnote{Mona had to pass the type argument \coqdocvar{M A} to the higher-ranked functional argument \coqdocvar{f} used in the application of \coqdocvar{join} because Coq was not able to infer it.}%.
*)
(* begin hide *)
Variable M : Type -> Type.
Variable M__M : Monad M.
(* end hide *)
Definition induce A (f : forall X, F X -> M X) (fx : Free C__F A) : M A :=
fold_free (fun x => ret x) (fun x => join (f (M A) x)) fx.
(* begin hide *)
End Fold.
Arguments induce {_} {_} {_} {_} {_}.
(* end hide *)
(**
To test her current setup, Mona implemented the following functions that map instances of [Free] to the identity and the maybe monad, respectively.
*)
Definition zero_to_id A (zx : Zero A) : Id A := match zx with end.
Definition free_to_id A (fx : Free C__Zero A) : Id A := induce zero_to_id fx.
Definition one_to_maybe A (ox : One A) : Maybe A := nothing.
Definition free_to_maybe A (fx : Free C__One A) : Maybe A := induce one_to_maybe fx.
(**
The functions [zero_to_id] and [one_to_maybe], respectively, map the functors [Zero] and [One] to the corresponding monads.
As there are no values of type [Zero A], Mona used an empty pattern match to get a value of type [Id A] to define [zero_to_id].
Mona was quite satisfied with her development so far, using containers she successfully defined a valid type definition for free monads in Coq.
However, Mona also realized an important point she had not considered before.
Namely, while for every monad [m] there exists a functor [f] with a homomorphism from [Free f] to [m], there is not necessarily a function from the monad back to the free monad, such that the homomorphism forms an isomorphism with this function.
For example, as mentioned by %\cite{swierstra2008data}%, the list monad is not a free monad in the sense that the list monad is not isomorphic to an instance of the free monad.
In order to prove statements about more complex effects Mona would have to be able to model monads like the list monad that were not free.
Mona realized that she could not resort to structural equality as some values that were structurally equal in the original monad were not structurally equal in the the presentation using [Free f].
This divergence is due to the fact that more values inhabit the resulting representation using [Free] than inhabit the original monad, if the original monad is not a free monad.
She observed, however, that she could use a custom equality for this kind of monad to solve this problem.
When proving a statement in a setting with a monad that was not a free monad, she would use a custom equality instead of using structural equality.
At first, defining this kind of custom equality felt like an involved task to Mona, because she had to compare two instances of the free monad that used a container extension as functor.
However, Mona realized that she could define the equality by means of [induce] and an equality on the original monad.
That is, in order to compare two instances of the free monad she could use the homomorphism to map these instances to the original monad and use structural equality on the monadic terms.
This way she was able to model an effect that is not free.
In a nutshell, Mona would interpret the [Free] values as their monadic representatives and compare the monadic values using structural equality.
In another round of poring over her manuscripts, Mona found an implementation by %\cite{verbruggen2008polytypic}% that uses polynomial functors to
define generic data types in Coq.
The encoding using polynomial functors represents data types as combinations of four primitive constructors: unit, identity, product, and sum.
One disadvantage of this encoding is that many interesting data types cannot be represented: there is no possibility to represent function types at least not without running into problems concerning the strict positivity restriction, again.
In contrast, Mona could reason about all monads [m] that are free monads in the sense that there exists a functor [f] such that [m] is isomorphic to [Free f].
Mona had the additional restriction that [f] had to be a container.
For example, identity, maybe and error are free monads whose functors are containers.
If a monad [m] is not a free monad, that is, it is not isomorphic to some instance of [Free], Mona could still reason about it using a custom equality as long as the functor can be modeled using a container.
For example, the state monad is not a free monad but can be modeled using a container.
Mona found implementations of more involved effects like state using the free monad in combination with a container.%\footnote{For example, Mona found an implementation of the state monad in Agda using a free monad and containers (\url{https://github.com/agda/agda-stdlib/blob/v2.4.0/README/Container/FreeMonad.agda}).}%
Mona was quite astonished by the variety of monads she could model in her current setting.
Then she once again considered the continuation monad that she failed to instantiate in the beginning of her journey.
Indeed, it was still not possible to model the continuation monad using a container extension.
It was a bit of a pity for Mona that she could not represent the continuation monad, nevertheless, for now, she was content with the variety of monads that she could represent with her encoding.
* A Simple Proof
%\lettrine{T}{he}% next morning, Mona tackled the definition she struggled with in the beginning and that started this whole journey in the first place: a list with polymorphic elements and constructors with monadic components.
Instead of using a polymorphic type constructor for the monadic effect, Mona parametrized the definition of a monadic list over a container [C__F] and applied the type [Free C__F A] to the arguments of the [cons] constructor.
*)
(* begin hide *)
Section Lists.
Variable F : Type -> Type.
(* end hide *)
Inductive List (C__F : Container F) A :=
| nil : List C__F A
| cons : Free C__F A -> Free C__F (List C__F A) -> List C__F A.
(* begin hide *)
End Lists.
Arguments nil {_} {_} {_}.
Section List_ind.
Variable F : Type -> Type.
Variable C__F : Container F.
Section ForFree.
Inductive ForFree A (P : A -> Prop) : Free C__F A -> Prop :=
| For_pure : forall (x : A), P x -> ForFree P (pure x)
| For_impure : forall (s : Shape) (pf : Pos s -> Free C__F A),
(forall p, ForFree P (pf p)) -> ForFree P (impure (ext s pf)).
Lemma ForFree_bind :
forall A B (fx : Free C__F A) (f: A -> Free C__F B) (g: A -> Free C__F B),
ForFree (fun x => f x = g x) fx -> fx >>= f = fx >>= g.
Proof.
intros.
induction H; simpl.
- apply H.
- repeat apply f_equal.
apply functional_extensionality; intros.
apply H0.
Qed.
Inductive InFree A : A -> Free C__F A -> Prop :=
| In_Pure : forall x, InFree x (pure x)
| In_Impure: forall x (s : Shape) (pf : Pos s -> Free C__F A),
(exists p, InFree x (pf p)) -> InFree x (impure (ext s pf)).
Lemma ForFree_forall :
forall A (P : A -> Prop) (fx : Free C__F A),
ForFree P fx <-> (forall (x : A), InFree x fx -> P x).
Proof.
intros A P fx.
intuition.
- induction H.
+ inversion H0; subst; assumption.