508 *} |
508 *} |
509 |
509 |
510 section {* Combinators\label{sec:combinators} *} |
510 section {* Combinators\label{sec:combinators} *} |
511 |
511 |
512 text {* |
512 text {* |
513 Perhaps one of the most puzzling aspect for a beginner when reading |
513 For beginners, perhaps the most puzzling parts in the existing code of Isabelle are |
514 existing Isabelle code special purpose combinators. At first they |
514 the combinators. At first they seem to greatly obstruct the |
515 seem to obstruct the comprehension of the code, but after getting familiar |
515 comprehension of the code, but after getting familiar with them, they |
516 with them they actually ease the understanding and also the programming. |
516 actually ease the understanding and also the programming. |
517 |
517 |
518 \begin{readmore} |
518 \begin{readmore} |
519 The most frequently used combinator are defined in the files @{ML_file "Pure/library.ML"} |
519 The most frequently used combinator are defined in the files @{ML_file "Pure/library.ML"} |
520 and @{ML_file "Pure/General/basics.ML"}. |
520 and @{ML_file "Pure/General/basics.ML"}. |
521 \end{readmore} |
521 \end{readmore} |
523 The simplest combinator is @{ML I} which is just the identity function. |
523 The simplest combinator is @{ML I} which is just the identity function. |
524 *} |
524 *} |
525 |
525 |
526 ML{*fun I x = x*} |
526 ML{*fun I x = x*} |
527 |
527 |
528 text {* Another combinator is @{ML K}, defined as *} |
528 text {* Another simple combinator is @{ML K}, defined as *} |
529 |
529 |
530 ML{*fun K x = fn _ => x*} |
530 ML{*fun K x = fn _ => x*} |
531 |
531 |
532 text {* |
532 text {* |
533 It ``wraps'' a function around the argument @{text "x"}. However, this |
533 It ``wraps'' a function around the argument @{text "x"}. However, this |
534 function ignores its argument. |
534 function ignores its argument. So @{ML K} defines a constant function |
535 |
535 returning @{text x}. |
536 The next combinator is reverse application, @{ML "(op |>)"}, defined as |
536 |
|
537 The next combinator is reverse application, @{ML "|>"}, defined as |
537 *} |
538 *} |
538 |
539 |
539 ML{*fun x |> f = f x*} |
540 ML{*fun x |> f = f x*} |
540 |
541 |
541 text {* While just syntactic sugar for the usual function application, |
542 text {* While just syntactic sugar for the usual function application, |
552 which increments the argument @{text x} by 5. It does this by first incrementing |
553 which increments the argument @{text x} by 5. It does this by first incrementing |
553 the argument by 1 (Line 2); then storing the result in a pair (Line 3); taking |
554 the argument by 1 (Line 2); then storing the result in a pair (Line 3); taking |
554 the first component of the pair (Line 4) and finally incrementing the first |
555 the first component of the pair (Line 4) and finally incrementing the first |
555 component by 4 (Line 5). This kind of cascading manipulations of values is quite |
556 component by 4 (Line 5). This kind of cascading manipulations of values is quite |
556 common when dealing with theories (for example by adding a definition, followed by |
557 common when dealing with theories (for example by adding a definition, followed by |
557 lemmas and so on). Writing the function @{ML inc_by_five} using the reverse |
558 lemmas and so on). It should also be familiar to anyone who has used Haskell's |
|
559 do-notation. Writing the function @{ML inc_by_five} using the reverse |
558 application is much clearer than writing |
560 application is much clearer than writing |
559 *} |
561 *} |
560 |
562 |
561 ML{*fun inc_by_five x = fst ((fn x => (x, x)) (x + 1)) + 4*} |
563 ML{*fun inc_by_five x = fst ((fn x => (x, x)) (x + 1)) + 4*} |
562 |
564 |
573 val y3 = fst y2 |
575 val y3 = fst y2 |
574 val y4 = y3 + 4 |
576 val y4 = y3 + 4 |
575 in y4 end*} |
577 in y4 end*} |
576 |
578 |
577 text {* |
579 text {* |
|
580 Another reason why the let-bindings in the code above are better to be |
|
581 avoided: it is more than easy to get the intermediate values wrong! |
|
582 |
|
583 |
578 (FIXME: give a real world example involving theories) |
584 (FIXME: give a real world example involving theories) |
579 |
585 |
580 Similarly, the combinator @{ML "(op #>)"} is the reverse function |
586 Similarly, the combinator @{ML "#>"} is the reverse function |
581 composition. It can be used to define functions as follows |
587 composition. It can be used to define functions as follows |
582 *} |
588 *} |
583 |
589 |
584 ML{*val inc_by_six = |
590 ML{*val inc_by_six = |
585 (fn x => x + 1) |
591 (fn x => x + 1) |
586 #> (fn x => x + 2) |
592 #> (fn x => x + 2) |
587 #> (fn x => x + 3)*} |
593 #> (fn x => x + 3)*} |
588 |
594 |
589 text {* |
595 text {* |
590 which is the function composed of first the increment-by-one function and then |
596 which is the function composed of first the increment-by-one function and then |
591 increment-by-two, followed by increment-by-three. Applying 6 to this function |
597 increment-by-two, followed by increment-by-three. |
592 yields |
598 |
593 |
599 The remaining combinators described in this section add convenience for the |
594 @{ML_response [display,gray] "inc_by_six 6" "12"} |
600 ``waterfall method'' of writing functions. The combinator @{ML tap} allows |
595 |
601 one to get hold of an intermediate result (to do some side-calculations for |
596 as expected. |
602 instance). The function |
597 |
603 |
598 The remaining combinators add convenience for the ``waterfall method'' |
604 *} |
599 of writing functions. The combinator @{ML tap} allows one to get |
605 |
600 hold of an intermediate result (to do some side-calculations for instance). |
606 ML %linenumbers{*fun inc_by_three x = |
601 The function *} |
|
602 |
|
603 ML{*fun inc_by_three x = |
|
604 x |> (fn x => x + 1) |
607 x |> (fn x => x + 1) |
605 |> tap (fn x => tracing (makestring x)) |
608 |> tap (fn x => tracing (makestring x)) |
606 |> (fn x => x + 2)*} |
609 |> (fn x => x + 2)*} |
607 |
610 |
608 text {* increments the argument first by one and then by two. In the middle, |
611 text {* increments the argument first by one and then by two. In the middle, |
609 however, it uses @{ML tap} for printing the ``plus-one'' intermediate |
612 however, it uses @{ML tap} for printing the ``plus-one'' intermediate |
610 result inside the tracing buffer. |
613 result inside the tracing buffer (Line 3). The function @{ML tap} can only |
611 |
614 be used for side-calculations, because any value that is computed cannot |
612 The combinator @{ML "(op `)"} is similar, but applies a function to the value |
615 be merged back into the ``main waterfall''. To do this, the next combinator |
613 and returns the result together with the original value (as pair). For example |
616 can be used. |
614 the following function takes @{text x} as argument, and then first |
617 |
615 increments @{text x}, but also keeps @{text x}. The intermediate result is |
618 The combinator @{ML "`"} is similar to @{ML tap}, but applies a function to the value |
616 therefore the pair @{ML "(x + 1,x)" for x}. The function then increments the |
619 and returns the result together with the value (as a pair). For example |
617 right-hand component of the pair. |
620 the function |
618 *} |
621 *} |
619 |
622 |
620 ML{*fun inc_as_pair x = |
623 ML{*fun inc_as_pair x = |
621 x |> `(fn x => x + 1) |
624 x |> `(fn x => x + 1) |
622 |> (fn (x, y) => (x, y + 1))*} |
625 |> (fn (x, y) => (x, y + 1))*} |
623 |
626 |
624 text {* |
627 text {* |
625 The combinators @{ML "(op |>>)"} and @{ML "(op ||>)"} are defined for |
628 takes @{text x} as argument, and then first |
|
629 increments @{text x}, but also keeps @{text x}. The intermediate result is |
|
630 therefore the pair @{ML "(x + 1, x)" for x}. The function then increments the |
|
631 right-hand component of the pair. So finally the result will be |
|
632 @{ML "(x + 1, x + 1)" for x}. |
|
633 |
|
634 The combinators @{ML "|>>"} and @{ML "||>"} are defined for |
626 functions manipulating pairs. The first applies the function to |
635 functions manipulating pairs. The first applies the function to |
627 the first component of the pair, defined as: |
636 the first component of the pair, defined as: |
628 *} |
637 *} |
629 |
638 |
630 ML{*fun (x, y) |>> f = (f x, y)*} |
639 ML{*fun (x, y) |>> f = (f x, y)*} |
634 *} |
643 *} |
635 |
644 |
636 ML{*fun (x, y) ||> f = (x, f y)*} |
645 ML{*fun (x, y) ||> f = (x, f y)*} |
637 |
646 |
638 text {* |
647 text {* |
|
648 With the combinator @{ML "|->"} you can re-combine the elements from a pair. |
|
649 This combinator is defined as |
|
650 *} |
|
651 |
|
652 ML{*fun (x, y) |-> f = f x y*} |
|
653 |
|
654 text {* and can be used to write the following version of the @{text double} function *} |
|
655 |
|
656 ML{*fun double x = |
|
657 x |> (fn x => (x, x)) |
|
658 |-> (fn x => fn y => x + y)*} |
|
659 |
|
660 text {* |
|
661 Recall that @{ML "|>"} is the reverse function applications. The related |
|
662 reverse function composition is @{ML "#>"}. In fact all combinators @{ML "|->"}, |
|
663 @{ML "|>>"} and @{ML "||>"} described above have related combinators for function |
|
664 composition, namely @{ML "#->"}, @{ML "#>>"} and @{ML "##>"}. In this |
|
665 way, the function @{text double} can also be written as |
|
666 *} |
|
667 |
|
668 ML{*val double = |
|
669 (fn x => (x, x)) |
|
670 #-> (fn x => fn y => x + y)*} |
|
671 |
|
672 |
|
673 text {* |
|
674 |
|
675 |
|
676 |
639 (FIXME: find a good exercise for combinators) |
677 (FIXME: find a good exercise for combinators) |
640 *} |
678 *} |
641 |
679 |
642 end |
680 end |