diff --git a/.github/workflows/build-pdf.yml b/.github/workflows/build-pdf.yml index 494d91817..4e357d4fd 100644 --- a/.github/workflows/build-pdf.yml +++ b/.github/workflows/build-pdf.yml @@ -39,7 +39,7 @@ jobs: name: PDF files and LaTeX logs path: | ./sofp-src/sofp*.pdf - ./sofp-src/book_cover/sofp-3page-cover.pdf + ./sofp-src/sofp*-3page-cover.pdf ./sofp-src/sofp-logs.tar.bz2 if-no-files-found: error @@ -67,7 +67,19 @@ jobs: asset_name: sofp.pdf asset_content_type: application/pdf - - name: Upload sofp-draft.pdf + - name: Upload sofp-10pt.pdf + if: contains(github.ref, 'refs/tags/v') + id: upload-sofp + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./sofp-src/sofp-10pt.pdf + asset_name: sofp-10pt.pdf + asset_content_type: application/pdf + + - name: Upload sofp-vol1.pdf if: contains(github.ref, 'refs/tags/v') id: upload-sofp-draft uses: actions/upload-release-asset@v1 @@ -75,8 +87,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./sofp-src/sofp-draft.pdf - asset_name: sofp-draft.pdf + asset_path: ./sofp-src/sofp-vol1.pdf + asset_name: sofp-vol1.pdf asset_content_type: application/pdf - name: Upload sofp-3page-cover.pdf @@ -91,3 +103,15 @@ jobs: asset_name: sofp-3page-cover.pdf asset_content_type: application/pdf + - name: Upload sofp-vol1-3page-cover.pdf + if: contains(github.ref, 'refs/tags/v') + id: upload-sofp-vol1-cover + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./sofp-src/sofp-vol1-3page-cover.pdf + asset_name: sofp-vol1-3page-cover.pdf + asset_content_type: application/pdf + diff --git a/README.md b/README.md index 9e89e434f..337758e17 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,8 @@ on-demand printing at lulu.com or elsewhere. If you want to build from source, currently you need `LyX` 2.3.x and `pdftk` installed. +Change to the directory `sofp-src`. + The command `bash make_sofp_pdf.sh` builds PDF files `sofp.pdf` and `sofp-draft.pdf`. The first file is a full draft with some unfinished chapters, the second file contains only finished and proofread chapters. diff --git a/sofp-src/README.md b/sofp-src/README.md index 925d23a33..79339b29e 100644 --- a/sofp-src/README.md +++ b/sofp-src/README.md @@ -10,7 +10,7 @@ with the goal of building theoretical foundations that are valuable to practitio At this time, the video tutorials are obsolete because the book already covers all their material and much more. Some notations in the video tutorials have been changed in the book, and some mistakes have been corrected. -The material below is kept for reference only. Refer to the book for current status. +The material below is obsolete and kept for reference only. Refer to the book for current status. ## Scope of the tutorial series @@ -248,7 +248,7 @@ Contents in brief: - How to infer the most generic (parametric) types for higher-order functions - Worked examples and exercises -**Note**: There was an error in the last exercise as shown in the video -- it had no solution. +**Note**: There was an error in the last exercise as shown in the video -- it had no solutions. Please use the exercises from the current version of the PDF slides. ## Chapter 3, part 2: Disjunction types diff --git a/sofp-src/book_cover/sofp-back-cover-no-bg.tex.src b/sofp-src/book_cover/sofp-back-cover-no-bg.tex.src index 1e5e5b56a..fd122722d 100644 --- a/sofp-src/book_cover/sofp-back-cover-no-bg.tex.src +++ b/sofp-src/book_cover/sofp-back-cover-no-bg.tex.src @@ -1,6 +1,6 @@ \onecolumn \thispagestyle{empty} -\newgeometry{top=3cm, left=2.5cm, right=3.9cm,bottom=2cm} +\newgeometry{top=2cm, left=2.5cm, right=3.9cm,bottom=1cm} \begin{wrapfigure}{l}{0.4\columnwidth} \includegraphics[width=0.4\columnwidth]{monads_evil_face} @@ -19,15 +19,15 @@ to other FP languages. The book's topics include working with FP-style collections; reasoning about recursive functions and types; the Curry-Howard correspondence; laws, structural -analysis, and code for functors, monads, and other typeclasses built upon exponential-polynomial data types; techniques -of symbolic derivation and proof; +analysis, and code for functors, monads, and other typeclasses based on exponential-polynomial data types; +techniques of symbolic derivation and proof; free typeclass constructions; and parametricity theorems. Long and difficult, yet boring explanations are logically developed in excruciating detail through NUMBEROFCODESNIPPETS Scala code snippets, NUMBEROFSTATEMENTS statements with step-by-step -derivations, NUMBEROFDIAGRAMS diagrams, NUMBEROFEXAMPLES solved examples +derivations, NUMBEROFDIAGRAMS diagrams, NUMBEROFEXAMPLES examples with tested Scala code, and NUMBEROFEXERCISES exercises. Discussions build upon each chapter's material further. @@ -45,11 +45,12 @@ and explains the use of parametricity for reasoning about the Church encoding an Readers should have a working knowledge of programming; e.g., be able to write code that prints the number of distinct words in a sentence. The difficulty of this book's mathematical derivations -is at the level of undergraduate calculus, similar to that of multiplying +is at the level of undergraduate multivariate calculus, similar to that of multiplying matrices or simplifying the expressions: \[ \frac{1}{x-2}-\frac{1}{x+2}\quad\text{ and }\quad\frac{d}{dx}\left((x+1)f(x)e^{-x}\right)\quad. \] -Sergei Winitzki received a Ph.D. in theoretical physics. After a career in academic research, he currently works as a software engineer. +The author received a Ph.D. in theoretical physics. After a career in academic research, he works as a software engineer. +%\vspace{0.2cm}\hspace*{\fill}\includegraphics[scale=1.0]{barcode} diff --git a/sofp-src/book_cover/sofp-cover-page-no-bg.tex b/sofp-src/book_cover/sofp-cover-page-no-bg.tex index 8aae8133d..af2459807 100644 --- a/sofp-src/book_cover/sofp-cover-page-no-bg.tex +++ b/sofp-src/book_cover/sofp-cover-page-no-bg.tex @@ -11,6 +11,8 @@ \vspace{0.4in} \centerline{\fontsize{25pt}{25pt}\selectfont{\textit{A tutorial, with examples in Scala}}} +% End of title. + \begin{textblock*}{8cm}(11.5cm,21cm) \fontsize{18pt}{18pt}\selectfont{\textsf{Sergei Winitzki}} \end{textblock*} diff --git a/sofp-src/book_cover/sofp-cover-parameters.tex b/sofp-src/book_cover/sofp-cover-parameters.tex index bd9f76cac..1a454f3d0 100644 --- a/sofp-src/book_cover/sofp-cover-parameters.tex +++ b/sofp-src/book_cover/sofp-cover-parameters.tex @@ -18,7 +18,7 @@ % -- Ingram: page count MUST be divisible by 2. % -- Blurb : page count MUST be divisible by 6. % Add blank pages as needed in final PDF generations! -\pgfmathsetmacro\TotalPageCount{875}% Must be manually entered +\pgfmathsetmacro\TotalPageCount{TOTALPAGES}% Must be manually entered \pgfmathsetmacro\PaperWidthPt{7.444in}% \pgfmathsetmacro\PaperHeightPt{9.68in}% diff --git a/sofp-src/book_cover/sofp-make-cover.sh b/sofp-src/book_cover/sofp-make-cover.sh index 37d01e667..471442b19 100644 --- a/sofp-src/book_cover/sofp-make-cover.sh +++ b/sofp-src/book_cover/sofp-make-cover.sh @@ -1,8 +1,9 @@ #!/bin/bash - +# Prepare the three cover pages in parallel. for f in sofp-spine sofp-back-cover sofp-front-cover; do -pdflatex --interaction=batchmode "$f" +pdflatex --interaction=batchmode "$f" & done +wait # Wait until all 3 cover pages are done. pdflatex --interaction=batchmode sofp-3page-cover diff --git a/sofp-src/book_cover/sofp-spine.tex b/sofp-src/book_cover/sofp-spine.tex index 4bfa70a4b..76192ae5c 100644 --- a/sofp-src/book_cover/sofp-spine.tex +++ b/sofp-src/book_cover/sofp-spine.tex @@ -27,7 +27,8 @@ %\TileWallPaper{\CoverHeight in}{\SpineWidth in + 12cm}{spine-background.png} %\pagecolor{yellow!20} \vspace*{\fill} -\begin{minipage}{0.7\linewidth}\begin{center}\vspace*{-1.0mm} \fontsize{32pt}{32pt}\selectfont\textbf{{The Science of Functional Programming}}\end{center}\end{minipage} -\begin{minipage}{0.3\linewidth}\begin{center}\vspace*{-1.0mm} \fontsize{20pt}{20pt}\selectfont\textsf{Winitzki}\end{center}\end{minipage} +\begin{minipage}{0.7\linewidth}\begin{center}\vspace*{-1.0mm} \fontsize{16pt}{16pt}\selectfont\textbf{{% +The Science of Functional Programming}}\end{center}\end{minipage} +\begin{minipage}{0.3\linewidth}\begin{center}\vspace*{-1.0mm} \fontsize{12pt}{12pt}\selectfont\textsf{Winitzki}\end{center}\end{minipage} \vspace*{\fill} \end{document} diff --git a/sofp-src/book_cover/vol1_isbn_barcode.pdf b/sofp-src/book_cover/vol1_isbn_barcode.pdf new file mode 100644 index 000000000..25e999d14 Binary files /dev/null and b/sofp-src/book_cover/vol1_isbn_barcode.pdf differ diff --git a/sofp-src/book_cover/vol2_isbn_barcode.pdf b/sofp-src/book_cover/vol2_isbn_barcode.pdf new file mode 100644 index 000000000..0c3762ff7 Binary files /dev/null and b/sofp-src/book_cover/vol2_isbn_barcode.pdf differ diff --git a/sofp-src/book_cover/vol3_isbn_barcode.pdf b/sofp-src/book_cover/vol3_isbn_barcode.pdf new file mode 100644 index 000000000..fee395055 Binary files /dev/null and b/sofp-src/book_cover/vol3_isbn_barcode.pdf differ diff --git a/sofp-src/check_and_make_draft.sh b/sofp-src/check_and_make_draft.sh index 270d18408..f2df0a379 100644 --- a/sofp-src/check_and_make_draft.sh +++ b/sofp-src/check_and_make_draft.sh @@ -99,3 +99,9 @@ function create_draft { # Create the lulu.com draft file by selecting the chapters that have been proofread. create_draft $name $draft.pdf + +# Attach sources to the draft file. +if test -s $name-src.tar.bz2 && test -s $draft.pdf; then "$pdftk" $draft.pdf attach_files "$name-src.tar.bz2" output $draft-src.pdf +else + echo Not attaching sources to draft since no source file $name-src.tar.bz2 is found or no $draft.pdf is found. +fi diff --git a/sofp-src/excluded_words b/sofp-src/excluded_words index a4ea097ff..c0d99a6cb 100644 --- a/sofp-src/excluded_words +++ b/sofp-src/excluded_words @@ -540,6 +540,7 @@ ^bifunctorQ$ ^bifunctors$ ^bigM$ +^bijective$ ^bimap$ ^binSearch$ ^binaryOp$ @@ -1286,6 +1287,7 @@ ^subarray$ ^subarrays$ ^subarrays3$ +^subclasses$ ^subexpressions$ ^subindices$ ^subsequence$ diff --git a/sofp-src/make_sofp_pdf.sh b/sofp-src/make_sofp_pdf.sh index 753739954..cc2c90910 100644 --- a/sofp-src/make_sofp_pdf.sh +++ b/sofp-src/make_sofp_pdf.sh @@ -2,7 +2,8 @@ # Requires pdftk and lyx. # Get lyx from www.lyx.org -# For Mac, get pdftk from https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/pdftk_server-2.02-mac_osx-10.11-setup.pkg +# For Mac, get pdftk from https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/pdftk_server-2.02-mac_osx-10.11-setup.pkg or else it may not work +# For Windows, get pdftk from https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/pdftk_free-2.02-win-setup.exe name="sofp" @@ -63,7 +64,7 @@ function add_color { local texsrc="$1" # Insert color comments into displayed equation arrays. Also, in some places the green color was inserted; replace by `greenunder`. # Example of inserted color: {\color{greenunder}\text{outer-interchange law for }M:}\quad & - LC_ALL=C sed -i.bak -e 's|\\color{green}|\\color{greenunder}|; s|^\(.*\\text{[^}]*}.*:\)\( *\\quad \& \)|{\\color{greenunder}\1}\2|' "$texsrc" + LC_ALL=C sed -i.bak -e 's|\\color{green}|\\color{greenunder}|; s|^\(.*\\text{[^}]*}.*:\)\( *\\quad \& \)|{\\color{greenunder}\1}\2|; s|\(\& *\\quad\)\(.*\\text{[^}]*}.*: *\)\(\\quad\\\\\)$|\1{\\color{greenunder}\2}\3|' "$texsrc" # Insert color background into all displayed equations. This is disabled because it does not always produce visually good results. if false; then LC_ALL=C sed -i.bak -E -e ' s!\\begin\{(align.?|equation)\}!\\begin{empheq}[box=\\mymathbgbox]{\1}!; s!\\end\{(align.?|equation)\}!\\end{empheq}!; ' "$texsrc" @@ -156,12 +157,14 @@ for f in sofp-cover-page-no-bg.tex sofp-cover-page.tex sofp-back-cover.tex sofp- # Check whether the sources have changed. If so, create a new sources archive and a new PDF file. add_source_hashes $name.tex +# Prepare "$name-src.tar.bz2" with all sources. assemble_sources & echo "Creating a full PDF file..." -make_pdf_with_index "$name" # Output is $name.pdf, main file is $name.tex, and other .tex files are \include'd. +make_pdf_with_index "$name" # Output is $name.pdf, main file is $name.tex, and other .tex files are \include'd. +# Wait until assemble_sources is finished. wait if ! test -s "$name".pdf; then @@ -192,18 +195,21 @@ total_pages=`pdfPages "$name".pdf` echo Result is "$name.pdf", size `kbSize "$name.pdf"` bytes, with $total_pages pages. +bash prepare_10point.sh "$pdftk" & + +bash prepare_volume.sh 1 "$pdftk" & +bash prepare_volume.sh 2 "$pdftk" & +bash prepare_volume.sh 3 "$pdftk" & + +bash spelling_check.sh & + # Create the "clean draft" pdf file by selecting the chapters that have been proofread. # Check page counts in the draft file and in individual chapters. -bash check_and_make_draft.sh -draft_pages=`pdfPages "$draft".pdf` +#bash check_and_make_draft.sh +#draft_pages=`pdfPages "$draft".pdf` bash check-consistent-labels.sh bash check-lines_with_displaymath_in_new_paragraph.sh -# Attach sources to the draft file. -if test -s $name-src.tar.bz2 && test -s $draft.pdf; then "$pdftk" $draft.pdf attach_files "$name-src.tar.bz2" output $draft-src.pdf -else - echo Not attaching sources to draft since no source file $name-src.tar.bz2 is found or no $draft.pdf is found. -fi if [ x"$1" == x-nolulu ]; then # Create a pdf file without references to lulu.com and without lulu.com's ISBN. mv "$name".pdf "$name"-lulu.pdf @@ -215,20 +221,17 @@ if [ x"$1" == x-nolulu ]; then mv "$name"-lulu.pdf "$name".pdf fi -if [ x"$1" == x-print ]; then - # Create a full pdf without hyperlinks, for printing on paper. Remove the cover image from first page and the back cover image. - mv "$name.pdf" "$name-hyperlinks.pdf" - LC_ALL=C sed -i.bak -e 's|colorlinks=true|colorlinks=false|; s|\\input{sofp-cover-page}||; s|\\input{sofp-back-cover-page}||; ' $name.tex - make_pdf_with_index $name fast - mv "$name.pdf" "$name-nohyperlinks.pdf" - mv "$name-hyperlinks.pdf" "$name.pdf" -fi +# Prepare an entire PDF file without hyperlinks. +#if [ x"$1" == x-print ]; then +# bash remove_hyperlinks.sh $name +#fi -bash spelling_check.sh -# Prepare the full 3-page book covers. Use $total_pages and not $draft_pages since the printed file has all unedited content as well. -sed -i.bak -e "s|TOTALPAGES|$total_pages|" book_cover/sofp-cover-parameters.tex -(cd book_cover; bash sofp-make-cover.sh) +# Prepare the full 3-page book covers. Use $total_pages of the whole pdf file and not $draft_pages since the printed file has all unedited content as well. +bash prepare_cover.sh "$name.pdf" "$pdftk" & # Cleanup? #rm -f $name*{idx,ind,aux,dvi,ilg,out,toc,log,ps,lof,lot,data} + +# Wait for background jobs. +wait diff --git a/sofp-src/prepare_cover.sh b/sofp-src/prepare_cover.sh new file mode 100644 index 000000000..2a4aede2d --- /dev/null +++ b/sofp-src/prepare_cover.sh @@ -0,0 +1,19 @@ +# Prepare the printable PDF file of volume v of the book. v = 1, 2, 3. + +pdffile="$1" +pdftk="$2" + +function pdfPages { + local file="$1" + "$pdftk" "$file" dump_data | fgrep NumberOfPages | sed -e 's,^.* ,,' +} + +export LC_ALL=C + +total_pages=`pdfPages "$pdffile"` + +cd book_cover + +sed -i.bak -e "s|TOTALPAGES|$total_pages|" sofp-cover-parameters.tex + +bash sofp-make-cover.sh diff --git a/sofp-src/prepare_volume.sh b/sofp-src/prepare_volume.sh new file mode 100644 index 000000000..11e31ff55 --- /dev/null +++ b/sofp-src/prepare_volume.sh @@ -0,0 +1,139 @@ +# Prepare the printable PDF file of volume v of the book. v = 1, 2, 3. +# The files are designed for printing and will have no PDF hyperlinks. + +function pdfPages { + local file="$1" + "$pdftk" "$file" dump_data | fgrep NumberOfPages | sed -e 's,^.* ,,' +} + +vol1_ISBN="ISBN (vol.~1): 978-1-4710-4004-7" +vol1_ISBN_barcode="vol1_isbn_barcode.pdf" +vol1_url="https://www.lulu.com/shop/sergei-winitzki/the-science-of-functional-programming-part-i/paperback/product-dyyq2zm.html" + +vol2_ISBN="ISBN (vol.~2): 978-1-4461-9146-0" +vol2_ISBN_barcode="vol2_isbn_barcode.pdf" +vol2_url="https://www.lulu.com/shop/sergei-winitzki/the-science-of-functional-programming-part-ii/paperback/product-655e7wm.html" + +vol3_ISBN="ISBN (vol.~3): 978-1-4461-9136-1" +vol3_ISBN_barcode="vol3_isbn_barcode.pdf" +vol3_url="https://www.lulu.com/shop/sergei-winitzki/the-science-of-functional-programming-part-ii/paperback/product-p668z4q.html" + +v=$1 +pdftk=$2 + +dir=vol$v +name=sofp-$dir + +export LC_ALL=C + +rm -rf $dir +mkdir -p $dir/book_cover +cd $dir + +tar jxf ../sofp-src.tar.bz2 +mv sofp-src/* . + +cp ../sofp*.tex ../sofp.* . +cp ../book_cover/* ./book_cover/ + +cp book_cover/sofp-cover-parameters.tex.src book_cover/sofp-cover-parameters.tex + +function remove_part1 { + fgrep -v '\part{Introductory level}' | \ + fgrep -v '\include{sofp-nameless-functions}' +} + +function remove_part2 { + fgrep -v '\part{Intermediate level}' | \ + fgrep -v '\include{sofp-functors}' +} + +function remove_part3 { + fgrep -v '\part{Advanced level}' | \ + fgrep -v 'part{Discussions}' | \ + fgrep -v 'part{Appendixes}' | \ + fgrep -v '\appendix' | \ + fgrep -v '\include{sofp-summary}' | \ + fgrep -v '\include{sofp-appendices}' | \ + fgrep -v '\include{sofp-free-type}' +} + +# Set part, section, page counters. \setcounter{page}{1} and so on. +firstpart1=$(fgrep '\contentsline {part}' ../sofp.aux | tail -n +$v | head -1 | sed -e 's|.*{part.\([0-9]\)}.*|\1|') +firstpage1=$(fgrep '\contentsline {part}' ../sofp.aux | tail -n +$v | head -1 | sed -e 's|.*{\([0-9]*\)}{part.[0-9]}.*|\1|') +# Latex will increment those counters immediately. So, we decrement them here before setting. +firstpart=$((firstpart1-1)) +firstpage=$((firstpage1-0)) + +function get_chapter { + local c=$(fgrep '\contentsline {chapter}' "$1" | head -1 | sed -e 's|.*{chapter.\([0-9]*\)}.*|\1|') + echo $((c-1)) +} + +# Remove all content except for this volume. +case $v in +1) + firstchapter=$(get_chapter ../sofp-nameless-functions.aux) + echo "Detected previous chapter $firstchapter, first page $firstpage, previous part number $firstpart" + cat sofp.tex | remove_part2 | remove_part3 > $name.tex + sed -i.bak -e 's|\(of Functional Programming\)|\1, Part I|; s|\(\\part{.*}\)|\\setcounter{page}{'$firstpage'}\\setcounter{part}{'$firstpart'}\\setcounter{chapter}{'$firstchapter'}\1|;' $name.tex + sed -i.bak -e 's|% End of title.|\\vspace{0.2in}\\centerline{\\fontsize{20pt}{20pt}\\selectfont{Part I: Introductory level}}|' book_cover/sofp-cover-page-no-bg.tex + sed -i.bak -e 's|\(of Functional Programming\)|\1, Part I|;' book_cover/sofp-spine.tex + + # Replace ISBN information. + echo "Using volume $v ISBN '$vol1_ISBN'" + sed -i.bak -e 's|\({\\footnotesize{}\)ISBN: [^}]*\(}\\\\\)|\1'"$vol1_ISBN"'\2|;' $name.tex + # Add barcode to back cover. + sed -i.bak -e 's|%\(.*\){barcode}.*|\1{'$vol1_ISBN_barcode'}|' book_cover/sofp-back-cover-no-bg.tex + ;; + +2) + firstchapter=$(get_chapter ../sofp-functors.aux) + echo "Detected previous chapter $firstchapter, first page $firstpage, previous part number $firstpart" + cat sofp.tex | remove_part1 | remove_part3 > $name.tex + + sed -i.bak -e 's|\(of Functional Programming\)|\1, Part II|; s|\(\\part{.*}\)|\\setcounter{page}{'$firstpage'}\\setcounter{part}{'$firstpart'}\\setcounter{chapter}{'$firstchapter'}\1|;' $name.tex + sed -i.bak -e 's|% End of title.|\\vspace{0.2in}\\centerline{\\fontsize{20pt}{20pt}\\selectfont{Part II: Intermediate level}}|' book_cover/sofp-cover-page-no-bg.tex + sed -i.bak -e 's|\(of Functional Programming\)|\1, Part II|;' book_cover/sofp-spine.tex + + # Replace ISBN information. + echo "Using volume $v ISBN '$vol2_ISBN'" + sed -i.bak -e 's|\({\\footnotesize{}\)ISBN: [^}]*\(}\\\\\)|\1'"$vol2_ISBN"'\2|;' $name.tex + # Add barcode to back cover. + sed -i.bak -e 's|%\(.*\){barcode}.*|\1{'$vol2_ISBN_barcode'}|' book_cover/sofp-back-cover-no-bg.tex + ;; + +3) + firstchapter=$(get_chapter ../sofp-free-type.aux) + echo "Detected previous chapter $firstchapter, first page $firstpage, previous part number $firstpart" + cat sofp.tex | remove_part1 | remove_part2 > $name.tex + + sed -i.bak -e 's|\(of Functional Programming\)|\1, Part III|; s|\(\\part{.*}\)|\\setcounter{page}{'$firstpage'}\\setcounter{part}{'$firstpart'}\\setcounter{chapter}{'$firstchapter'}\1|;' $name.tex + sed -i.bak -e 's|% End of title.|\\vspace{0.2in}\\centerline{\\fontsize{20pt}{20pt}\\selectfont{Part III: Advanced level}}|' book_cover/sofp-cover-page-no-bg.tex + sed -i.bak -e 's|\(of Functional Programming\)|\1, Part III|;' book_cover/sofp-spine.tex + + # Replace ISBN information. + echo "Using volume $v ISBN '$vol3_ISBN'" + sed -i.bak -e 's|\({\\footnotesize{}\)ISBN: [^}]*\(}\\\\\)|\1'"$vol3_ISBN"'\2|;' $name.tex + # Add barcode to back cover. + sed -i.bak -e 's|%\(.*\){barcode}.*|\1{'$vol3_ISBN_barcode'}|' book_cover/sofp-back-cover-no-bg.tex + ;; + +esac + +cp book_cover/* . + +mv $name.tex sofp.tex + +# Disable PDF hyperlinks and remove covers. +LC_ALL=C sed -i.bak -e 's|colorlinks=true|colorlinks=false|; s|\\input{sofp-cover-page}||; s|\\input{sofp-back-cover-page}||; ' sofp.tex + +pdflatex --interaction=batchmode sofp +makeindex sofp.idx +cp ../*.aux . # Enable references to other chapters. +pdflatex --interaction=batchmode sofp + +mv sofp.pdf ../$name.pdf + +bash ../prepare_cover.sh ../$name.pdf "$pdftk" +mv book_cover/sofp-3page-cover.pdf ../$name-3page-cover.pdf diff --git a/sofp-src/remove_hyperlinks.sh b/sofp-src/remove_hyperlinks.sh new file mode 100644 index 000000000..f6a42b4b0 --- /dev/null +++ b/sofp-src/remove_hyperlinks.sh @@ -0,0 +1,9 @@ +name="$1" + # Create a full pdf without hyperlinks, for printing on paper. Remove the cover image from first page and the back cover image. + # Assume that all files have been processed, use fast processing. + mv "$name.pdf" "$name-hyperlinks.pdf" + LC_ALL=C sed -i.bak -e 's|colorlinks=true|colorlinks=false|; s|\\input{sofp-cover-page}||; s|\\input{sofp-back-cover-page}||; ' $name.tex + #make_pdf_with_index $name fast + pdflatex --interaction=batchmode "$name" + mv "$name.pdf" "$name-nohyperlinks.pdf" + mv "$name-hyperlinks.pdf" "$name.pdf" diff --git a/sofp-src/sofp-appendices.lyx b/sofp-src/sofp-appendices.lyx index d655d6982..afe96f210 100644 --- a/sofp-src/sofp-appendices.lyx +++ b/sofp-src/sofp-appendices.lyx @@ -24,6 +24,8 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} @@ -189,7 +191,7 @@ \renewcommand{\ogreaterthan}{\boxrightarrow} \renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -259,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -8609,7 +8611,7 @@ filter status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -14554,7 +14556,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -15626,7 +15628,7 @@ noprefix "false" status collapsed \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -20417,7 +20419,7 @@ This completes the proof of the relational parametricity theorem. \end_layout \begin_layout Subsection -Naturality laws and the wedge law follow from relational parametricity +Deriving the wedge law and the naturality laws from parametricity \end_layout \begin_layout Standard @@ -20520,45 +20522,9 @@ noprefix "false" \end_inset ). - -\end_layout - -\begin_layout Standard -For type signatures of natural transformations ( -\begin_inset Formula $\forall A.\,G^{A}\rightarrow H^{A}$ -\end_inset - -), the wedge law reduces to the naturality laws -\begin_inset space ~ -\end_inset - -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:naturality-law-for-functors" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -) or -\begin_inset space ~ -\end_inset - -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:naturality-law-for-contrafunctors" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -). - So, the naturality laws and the wedge law are shortcuts for using the parametri -city theorem without having to work with a fully general relational law. + For those type signatures, the dinaturality law and the wedge law are + shortcuts for using the parametricity theorem without having to work with + a fully general relational laws. \begin_inset Note Note status collapsed @@ -20790,7 +20756,41 @@ noprefix "false" \end_inset + For fully parametric natural transformations with type signatures of the + form +\begin_inset Formula $\forall A.\,G^{A}\rightarrow H^{A}$ +\end_inset + +, the wedge law is further simplified to the naturality laws +\begin_inset space ~ +\end_inset + +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:naturality-law-for-functors" +plural "false" +caps "false" +noprefix "false" +\end_inset + +) or +\begin_inset space ~ +\end_inset + +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:naturality-law-for-contrafunctors" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). + \end_layout \begin_layout Standard @@ -20799,14 +20799,15 @@ For certain more complicated type signatures, one can derive a property \begin_inset Quotes eld \end_inset -strong +strong dinaturality \begin_inset Quotes erd \end_inset - dinaturality. - This property is not expressed as a single equation and is stronger than - the wedge law but is still simpler to use than the full relational law. - In this book, strong dinaturality is needed only to prove Statements +. + This property is not expressed as a single equation and is more complicated + than the wedge law but is still simpler to use than the full relational + law. + In this book, strong dinaturality is used to prove Statements \begin_inset space ~ \end_inset @@ -21001,7 +21002,7 @@ literal "false" \emph on without \emph default - using relational approach parametricity) is in the paper + using relational parametricity) is in the paper \begin_inset Quotes eld \end_inset @@ -35288,7 +35289,7 @@ noprefix "false" status collapsed \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -37654,7 +37655,7 @@ noprefix "false" status collapsed \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -39281,7 +39282,7 @@ noprefix "false" status collapsed \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -47136,7 +47137,7 @@ name "sec:GFDL" \begin_layout Standard -\size footnotesize +\size scriptsize Version 1.2, November 2002 \end_layout diff --git a/sofp-src/sofp-appendices.tex b/sofp-src/sofp-appendices.tex index 6559d5fb1..bb2ae4541 100644 --- a/sofp-src/sofp-appendices.tex +++ b/sofp-src/sofp-appendices.tex @@ -1118,7 +1118,7 @@ \subsection{Naturality and dinaturality laws\label{sec:Naturality-laws-for-fully to certain profunctors $G$ and $H$. \subsubsection{Example \label{subsec:Example-derive-naturality-of-filter-from-dinaturality}\ref{subsec:Example-derive-naturality-of-filter-from-dinaturality} -(naturality law of \lstinline!filter!)\index{solved examples}} +(naturality law of \lstinline!filter!)\index{examples (with code)}} To derive the naturality law of \lstinline!filter!, express \lstinline!filter!\textsf{'}s type signature through profunctors $G$ and $H$ as: @@ -1876,7 +1876,7 @@ \subsubsection{Definition \label{subsec:Definition-pair-co-product-of-relations} operations $\boxtimes$, $\boxplus$, and $\ogreaterthan$ when applied to function graph relations. -\subsubsection{Example \label{subsec:Example-pair-product-pair-mapper-relation}\ref{subsec:Example-pair-product-pair-mapper-relation}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-pair-product-pair-mapper-relation}\ref{subsec:Example-pair-product-pair-mapper-relation}\index{examples (with code)}} For arbitrary given functions $f^{:A\rightarrow B}$ and $g^{:C\rightarrow D}$, show that: @@ -2102,7 +2102,7 @@ \subsubsection{Definition \label{subsec:Definition-relational-lifting}\ref{subse through ordinary function liftings $f^{\uparrow P}$ and $f^{\downarrow P}$ and the $\left(P,f\right)$-wedge condition. -\subsubsection{Example \label{subsec:Example-relational-lifting}\ref{subsec:Example-relational-lifting}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-relational-lifting}\ref{subsec:Example-relational-lifting}\index{examples (with code)}} Consider the function graph relation $\left$ of a given function $f^{:A\rightarrow B}$. Use Definition~\ref{subsec:Definition-relational-lifting} @@ -2955,7 +2955,7 @@ \subsubsection{Statement \label{subsec:Statement-main-relational-parametricity-1 This completes the proof of the relational parametricity theorem. -\subsection{Naturality laws and the wedge law follow from relational parametricity} +\subsection{Deriving the wedge law and the naturality laws from parametricity} A programmer usually needs to derive laws in terms of \emph{functions} rather than relations. To convert the relational naturality law~(\ref{eq:relational-naturality-law-simplified}) @@ -2969,19 +2969,19 @@ \subsection{Naturality laws and the wedge law follow from relational parametrici as an equation. Nevertheless, for many type signatures found in practice, this problem does not arise and the relational naturality law is reduced to the dinaturality law~(\ref{eq:dinaturality-law-for-profunctors}) -via the wedge law~(\ref{eq:wedge-law-for-profunctors}). - -For type signatures of natural transformations ($\forall A.\,G^{A}\rightarrow H^{A}$), -the wedge law reduces to the naturality laws~(\ref{eq:naturality-law-for-functors}) -or~(\ref{eq:naturality-law-for-contrafunctors}). So, the naturality -laws and the wedge law are shortcuts for using the parametricity theorem -without having to work with a fully general relational law. +via the wedge law~(\ref{eq:wedge-law-for-profunctors}). For those +type signatures, the dinaturality law and the wedge law are shortcuts +for using the parametricity theorem without having to work with a +fully general relational laws. For fully parametric natural transformations +with type signatures of the form $\forall A.\,G^{A}\rightarrow H^{A}$, +the wedge law is further simplified to the naturality laws~(\ref{eq:naturality-law-for-functors}) +or~(\ref{eq:naturality-law-for-contrafunctors}). For certain more complicated type signatures, one can derive a property -called the \textsf{``}strong\textsf{''} dinaturality. This property is not expressed -as a single equation and is stronger than the wedge law but is still -simpler to use than the full relational law. In this book, strong -dinaturality is needed only to prove Statements~\ref{subsec:relational-property-for-foldFn}, +called the \textsf{``}strong dinaturality\textsf{''}. This property is not expressed +as a single equation and is more complicated than the wedge law but +is still simpler to use than the full relational law. In this book, +strong dinaturality is used to prove Statements~\ref{subsec:relational-property-for-foldFn}, \ref{subsec:Statement-Church-encoding-recursive-type-covariant}, and~\ref{subsec:Statement-strong-dinaturality-property-of-fix}, while in all other places the wedge law is sufficient. This and the @@ -3004,8 +3004,8 @@ \subsection{Naturality laws and the wedge law follow from relational parametrici in the paper \texttt{\href{https://arxiv.org/pdf/1908.07776.pdf}{https://arxiv.org/pdf/1908.07776.pdf}}. A proof of the dinaturality law based on finding a syntactic normal form of fully parametric code (and \emph{without} using relational -approach parametricity) is in the paper \textsf{``}Dinatural terms in System -$F$\textsf{''} by J.~de Lataillade\index{Joachim de Lataillade}: see \texttt{\href{https://www.irif.fr/~delatail/dinat.pdf}{https://www.irif.fr/$\sim$delatail/dinat.pdf}}} + parametricity) is in the paper \textsf{``}Dinatural terms in System $F$\textsf{''} +by J.~de Lataillade\index{Joachim de Lataillade}: see \texttt{\href{https://www.irif.fr/~delatail/dinat.pdf}{https://www.irif.fr/$\sim$delatail/dinat.pdf}}} \subsubsection{Statement \label{subsec:Statement-wedge-law-from-parametricity}\ref{subsec:Statement-wedge-law-from-parametricity}} @@ -3978,7 +3978,7 @@ \subsubsection{Statement \label{subsec:Statement-lifting-function-relation-covar broad range of practically encountered functions have type signatures of that form. Here are some examples. -\subsubsection{Example \label{subsec:Example-strong-dinaturality-for-some-type-signatures}\ref{subsec:Example-strong-dinaturality-for-some-type-signatures}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-strong-dinaturality-for-some-type-signatures}\ref{subsec:Example-strong-dinaturality-for-some-type-signatures}\index{examples (with code)}} Let $F$ and $G$ be either a functor or a contrafunctor, independently of each other, and let $L^{X,Y}$ be any profunctor. Use Statement~\ref{subsec:Statement-post-wedge-entails-strong-dinaturality} @@ -4351,7 +4351,7 @@ \subsubsection{Statement \label{subsec:Statement-pullback-lifted-to-functor}\ref The strong dinaturality property of \lstinline!foldFn! can be proved by using Statement~\ref{subsec:Statement-functor-post-pre-wedge}: -\subsubsection{Example \label{subsec:Example-strong-dinaturality-proof-of-foldFn-law}\ref{subsec:Example-strong-dinaturality-proof-of-foldFn-law}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-strong-dinaturality-proof-of-foldFn-law}\ref{subsec:Example-strong-dinaturality-proof-of-foldFn-law}\index{examples (with code)}} Show that any fully parametric function $f:\forall A.\,L^{A\rightarrow A}\rightarrow A\rightarrow A$ is strongly dinatural when $L$ is any polynomial functor. @@ -4637,7 +4637,7 @@ \subsubsection{Statement \label{subsec:Statement-undisjunctive-type-constructors The following examples will help us build up intuition about non-disjunctive types. -\subsubsection{Example \label{subsec:Example-undisjunctive-type-constructors}\ref{subsec:Example-undisjunctive-type-constructors}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-undisjunctive-type-constructors}\ref{subsec:Example-undisjunctive-type-constructors}\index{examples (with code)}} \textbf{(a)} Show that $P^{A}\triangleq A\rightarrow A$ is lifting-to-true but \emph{not} lifting-to-false. @@ -6545,7 +6545,7 @@ \chapter{A humorous disclaimer} \chapter{GNU Free Documentation License\label{sec:GFDL} } -{\footnotesize{}Version 1.2, November 2002}{\footnotesize\par} +{\scriptsize{}Version 1.2, November 2002}{\scriptsize\par} {\tiny{}Copyright (c) 2000,2001,2002 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA}{\tiny\par} diff --git a/sofp-src/sofp-applicative.lyx b/sofp-src/sofp-applicative.lyx index bd52f573f..5b4666f82 100644 --- a/sofp-src/sofp-applicative.lyx +++ b/sofp-src/sofp-applicative.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -449,21 +464,6 @@ zip \end_inset method is equivalent to a function with the following type signature: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "55col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -476,26 +476,6 @@ def zip[A, B](la: List[A], lb: List[B]): List[(A, B)] \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset Formula \[ \text{zip}:\text{List}^{A}\times\text{List}^{B}\rightarrow\text{List}^{A\times B}\quad. @@ -617,6 +597,16 @@ noprefix "false" \end_inset +\begin_inset Index idx +status open + +\begin_layout Plain Layout +examples (with code) +\end_layout + +\end_inset + + \end_layout \begin_layout Standard @@ -2328,21 +2318,6 @@ Future \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "53.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2374,23 +2349,6 @@ val result: Future[Int] = for { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Recall that Scala's \begin_inset listings inline true @@ -2567,21 +2525,6 @@ map \end_inset methods like this: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "53.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2608,23 +2551,6 @@ val result: Future[Int] = \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -120baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The implementations of \begin_inset listings inline true @@ -3658,7 +3584,7 @@ F[A] \end_inset - defined by + defined by: \end_layout \begin_layout Standard @@ -4326,7 +4252,7 @@ mapN \end_inset - is then defined by + is then defined by: \begin_inset listings inline false status open @@ -4540,21 +4466,6 @@ fmap2 \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "54.3col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -4572,29 +4483,6 @@ def fmap2[A,B,C](f: A => B => C): L[A] => L[B] => L[C] \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -145baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -175baselineskip% -\end_inset - - \begin_inset Formula \begin{align*} & \text{fmap}:\left(A\rightarrow B\right)\rightarrow L^{A}\rightarrow L^{B}\quad,\\ @@ -4603,14 +4491,6 @@ def fmap2[A,B,C](f: A => B => C): L[A] => L[B] => L[C] \end_inset - -\begin_inset VSpace -170baselineskip% -\end_inset - - -\end_layout - -\begin_layout Standard If we try implementing \begin_inset listings inline true @@ -4699,21 +4579,6 @@ ap \end_inset and has the type signature: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "54.3col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -4726,29 +4591,6 @@ def ap[B, C](lf: L[B => C]): L[B] => L[C] \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset Formula \[ \text{ap}_{L}:L^{A\rightarrow B}\rightarrow L^{A}\rightarrow L^{B}\quad. @@ -4822,9 +4664,9 @@ fmap2 \end_inset -, we may rewrite this as +, we may rewrite this as: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout @@ -4834,8 +4676,7 @@ fmap2(f)(la)(lb) == ap(la.map(f))(lb) \end_inset -. - This expression is made clearer if we implement infix syntax for +This expression is made clearer if we implement infix syntax for \begin_inset listings inline true status open @@ -5933,14 +5774,10 @@ literal "false" \end_inset - A -\begin_inset Quotes eld -\end_inset - + For our purposes, a +\series bold corpus -\begin_inset Quotes erd -\end_inset - +\series default is a sequence of large chunks of text. Each chunk takes a long time to download, and storing the entire sequence in memory is impossible. @@ -8376,21 +8213,6 @@ average \end_inset equivalently as: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "28col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -8417,23 +8239,6 @@ val average = for { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent This equivalence comes from the fact that the case class \begin_inset listings inline true @@ -10048,24 +9853,6 @@ zipRight \end_inset within source lines, as shown in the previous section. -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "30col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -95baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -10097,21 +9884,7 @@ for { \end_inset - -\begin_inset VSpace -95baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -In the code shown at left, +In the code shown above, \begin_inset listings inline true status open @@ -11878,31 +11651,6 @@ fmap2 \end_inset in the code notation: -\begin_inset Note Comment -status open - -\begin_layout Plain Layout -precarious formatting -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement i -overhang 0in -width "34col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -170baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{0.4pc}\xyScaleX{2pc} & F^{B\rightarrow C}\ar[rd]\sp(0.5){\text{ap}^{B,C}}\\ @@ -11913,26 +11661,6 @@ F^{A}\ar[ru]\sp(0.45){f^{\uparrow L}}\ar[rr]\sb(0.43){\text{fmap}_{2}\,(f^{:A\ri \end_inset -\begin_inset VSpace -200baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset Formula \begin{align*} & \text{fmap}_{2}\,(f^{:A\rightarrow B\rightarrow C})=f^{\uparrow L}\bef\text{ap}^{B,C}\quad,\\ @@ -12802,7 +12530,7 @@ zip \end_inset The difference between the two sides of the law will disappear if we show - that + that: \begin_inset Formula \[ \text{eval}\bef f\overset{?}{=}\big((x\rightarrow x\bef f)\boxtimes\text{id}\big)\bef\text{eval}\quad. @@ -12919,7 +12647,7 @@ The remaining difference between \begin_inset Formula $\text{ap}\,(r)(p)$ \end_inset - will disappear if we show that + will disappear if we show that: \begin_inset Formula \[ \text{pair}^{:\left(A\rightarrow B\right)\rightarrow A\rightarrow\left(A\rightarrow B\right)\times A}\bef(x^{:A\rightarrow\left(A\rightarrow B\right)\times A}\rightarrow x\bef\text{eval})\overset{?}{=}\text{id}\quad. @@ -13869,7 +13597,7 @@ separation "3pt" shadowsize "4pt" framecolor "black" backgroundcolor "none" -status collapsed +status open \begin_layout Plain Layout \begin_inset listings @@ -13939,7 +13667,7 @@ separation "3pt" shadowsize "4pt" framecolor "black" backgroundcolor "none" -status collapsed +status open \begin_layout Plain Layout \begin_inset listings @@ -14934,7 +14662,7 @@ noprefix "false" \end_inset -We obtain: +Then we get: \begin_inset Formula \begin{align*} \text{left-hand side}:\quad & \text{zip}\,(\text{pu}_{L}(a)\times q)=\text{zip}\big((\text{wu}\triangleright(1\rightarrow a)^{\uparrow L})\times q\big)\\ @@ -15029,7 +14757,7 @@ Both sides contain the function \begin_inset Formula $A$ \end_inset - may be chosen arbitrarily) and obtain the function + may be chosen arbitrarily) and obtain the function: \begin_inset Formula \[ (1\times b^{:B}\rightarrow1\times b)^{\uparrow L}=\text{id}^{\uparrow L}=\text{id}^{:L^{\bbnum 1\times B}\rightarrow L^{\bbnum 1\times B}}\quad. @@ -17993,9 +17721,10 @@ After this replacement, we have three arguments ( , for which the associativity law holds by assumption. \begin_inset Note Comment -status collapsed +status open \begin_layout Plain Layout +*** do we need this? \begin_inset Formula \begin{align*} & \text{zip}_{L}\big(p\times\text{zip}_{L}(q\times r)\big)\triangleright\varepsilon_{1,23}^{\uparrow L}=\big((z_{1}\oplus z_{2}\oplus z_{3})+\bbnum 0\big)\triangleright\varepsilon_{1,23}^{\uparrow L}=(z_{1}\oplus z_{2}\oplus z_{3})+\bbnum 0\quad,\\ @@ -20351,7 +20080,7 @@ noprefix "false" \begin_inset Formula $S^{\bullet,\bullet}$ \end_inset - can be expressed as + can be expressed as: \begin_inset Formula \[ S^{A,R}=P_{0}^{A}+R^{A}\times(P_{1}^{A}+R^{A}\times(...\times(P_{n-1}^{A}+R^{A}\times P_{n}^{A})...))\quad, @@ -20673,7 +20402,7 @@ zip \end_inset - method with type signature + method with type signature: \begin_inset Formula \[ \text{zip}:(H^{A}\rightarrow A)\times(H^{B}\rightarrow B)\rightarrow H^{A\times B}\rightarrow A\times B\quad, @@ -25118,7 +24847,7 @@ f^{\downarrow P}=p^{:H^{A}\rightarrow G^{A}}\rightarrow f^{\uparrow H}\bef p\bef \end_layout \begin_layout Standard -To verify the left identity law, we need to show that +To verify the left identity law, we need to show that: \begin_inset Formula \[ h^{:H^{A}}\triangleright\big(\text{zip}_{P}(\text{wu}_{P}\times p^{:H^{A}\rightarrow G^{A}})\triangleright\text{ilu}^{\downarrow P}\big)\overset{?}{=}h\triangleright p=p(h)\quad. @@ -25347,16 +25076,16 @@ An example of a type constructor that \emph on cannot \emph default - be a profunctor is an + be a profunctor is a \begin_inset Quotes eld \end_inset -unfunctor +GADT \begin_inset Index idx status open \begin_layout Plain Layout -unfunctor +GADT \end_layout \end_inset @@ -25369,21 +25098,6 @@ unfunctor \end_layout \begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "47col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -25411,29 +25125,6 @@ final case class U3(b: String) extends U[Long] \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace 20baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset Formula \[ U^{A}\triangleq\text{Double}^{:U^{\bbnum 1}}+\text{String}^{:U^{\text{Int}}}+\text{String}^{:U^{\text{Long}}}\quad. @@ -25441,11 +25132,6 @@ U^{A}\triangleq\text{Double}^{:U^{\bbnum 1}}+\text{String}^{:U^{\text{Int}}}+\te \end_inset - -\end_layout - -\begin_layout Standard -\noindent The definition of this type constructor is \emph on not @@ -25941,7 +25627,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -28752,21 +28438,6 @@ wu \end_inset like this: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "29.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -28798,23 +28469,6 @@ def p[A]: A => A = { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent This function is defined for all types \begin_inset Formula $A$ \end_inset @@ -31339,7 +30993,7 @@ functorial \begin_inset Quotes erd \end_inset - typeclasses in view of category theory + typeclasses and category theory \begin_inset CommandInset label LatexCommand label name "subsec:The-pattern-of-functorial-typeclasses" @@ -31419,7 +31073,7 @@ noprefix "false" \end_inset -) with type signature +) with type signature: \begin_inset Formula \[ \text{liftOpt}:\left(A\rightarrow\bbnum 1+B\right)\rightarrow(F^{A}\rightarrow F^{B})\quad. @@ -32444,6 +32098,185 @@ noprefix "false" Use applicative functor constructions to avoid long proofs. \end_layout +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:Exercise-applicative-I-1-1-1" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-applicative-I-1-1-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +Implement a +\begin_inset Quotes eld +\end_inset + +padding +\begin_inset Quotes erd +\end_inset + + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zip +\end_layout + +\end_inset + + method for lists +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +List[A] +\end_layout + +\end_inset + + such that the shorter list is padded with the +\emph on +last +\emph default + value until its length is equal to that of the longer list. + Zipping with an empty list returns again an empty list. + A sample test: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def paddingZip[A, B](left: List[A], right: List[B]): List[(A, B)] = ??? +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\begin_layout Plain Layout + +scala> paddingZip(List(1, 2), List( +\begin_inset Quotes eld +\end_inset + +a +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +b +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +c +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +d +\begin_inset Quotes erd +\end_inset + +)) +\end_layout + +\begin_layout Plain Layout + +res0: List[(Int, String)] = List((1, +\begin_inset Quotes eld +\end_inset + +a +\begin_inset Quotes erd +\end_inset + +), (2, +\begin_inset Quotes eld +\end_inset + +b +\begin_inset Quotes erd +\end_inset + +), (2, +\begin_inset Quotes eld +\end_inset + +c +\begin_inset Quotes erd +\end_inset + +), (2, +\begin_inset Quotes eld +\end_inset + +d +\begin_inset Quotes erd +\end_inset + +)) +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\begin_layout Plain Layout + +scala> paddingZip(List(), List(1, 2, 3)) +\end_layout + +\begin_layout Plain Layout + +res1: List[(Unit, Int)] = List() +\end_layout + +\end_inset + +Prove that the laws of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zip +\end_layout + +\end_inset + + hold for this function. +\end_layout + \begin_layout Subsubsection Exercise \begin_inset CommandInset label @@ -33101,7 +32934,7 @@ The type constructor \begin_inset Formula $Q^{\bullet}$ \end_inset - is defined by + is defined by: \begin_inset Formula \[ Q^{A}\triangleq\left(A\rightarrow\text{Int}\right)\times A\times\left(A\rightarrow A\right)\quad. diff --git a/sofp-src/sofp-applicative.tex b/sofp-src/sofp-applicative.tex index 5f65cf3b2..29ad56c5d 100644 --- a/sofp-src/sofp-applicative.tex +++ b/sofp-src/sofp-applicative.tex @@ -23,17 +23,9 @@ \subsection{Generalizing the \texttt{zip} method from sequences to other types} \lstinline!zip! operation for sequences and other collections. For lists, the \lstinline!zip! method is equivalent to a function with the following type signature: - -\begin{wrapfigure}{l}{0.55\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} def zip[A, B](la: List[A], lb: List[B]): List[(A, B)] \end{lstlisting} - -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -\noindent \vspace{-0.5\baselineskip} \[ \text{zip}:\text{List}^{A}\times\text{List}^{B}\rightarrow\text{List}^{A\times B}\quad. \] @@ -49,7 +41,7 @@ \subsection{Generalizing the \texttt{zip} method from sequences to other types} operation later in this chapter. For now, let us look at some examples of implementing a \lstinline!zip! operation. -\subsubsection{Example \label{subsec:Example-applicative-not-monad}\ref{subsec:Example-applicative-not-monad}} +\subsubsection{Example \label{subsec:Example-applicative-not-monad}\ref{subsec:Example-applicative-not-monad}\index{examples (with code)}} A \lstinline!zip! method can be implemented for the type constructor $L^{A}\triangleq\bbnum 1+A\times A$: @@ -289,9 +281,6 @@ \subsection{Monadic programs with independent effects\label{subsec:Monadic-progr monadic programs where some source lines do not depend on previous results. As an example, consider the following monadic program involving \lstinline!Future!: - -\begin{wrapfigure}{l}{0.535\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} val result: Future[Int] = for { x <- Future { func1() } // func1() returns an Int. @@ -299,41 +288,29 @@ \subsection{Monadic programs with independent effects\label{subsec:Monadic-progr z <- Future { func3() } // Similarly for func3(). } yield x + y + z \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent Recall that Scala\textsf{'}s \lstinline!Future! values are computations -that have been scheduled to run on other threads (and may be already -running). In this example, the computations in \lstinline!func1()!, -\lstinline!func2()!, and \lstinline!func3()! do not depend on the -previously computed values (\lstinline!x! and \lstinline!y!). So, -we would like to run those computations in parallel. However, the -monadic program will create a second \lstinline!Future! value and -schedule the computation \lstinline!func2()! only after \lstinline!func1()! -is done. Similarly, \lstinline!func3()! will not start until \lstinline!func2()! -is done. This is because the functor block shown above is translated +Recall that Scala\textsf{'}s \lstinline!Future! values are computations that +have been scheduled to run on other threads (and may be already running). +In this example, the computations in \lstinline!func1()!, \lstinline!func2()!, +and \lstinline!func3()! do not depend on the previously computed +values (\lstinline!x! and \lstinline!y!). So, we would like to run +those computations in parallel. However, the monadic program will +create a second \lstinline!Future! value and schedule the computation +\lstinline!func2()! only after \lstinline!func1()! is done. Similarly, +\lstinline!func3()! will not start until \lstinline!func2()! is +done. This is because the functor block shown above is translated into \lstinline!flatMap! and \lstinline!map! methods like this: - -\begin{wrapfigure}{l}{0.535\columnwidth}% -\vspace{-0.9\baselineskip} \begin{lstlisting} val result: Future[Int] = Future { func1() }.flatMap { x => Future { func2() }.flatMap { y => Future { func3() }.map { z => x + y + z } } } \end{lstlisting} - -\vspace{-1.2\baselineskip} -\end{wrapfigure}% - -\noindent The implementations of \lstinline!flatMap! and \lstinline!map! -cannot detect whether the next computations actually depend on any -previously computed values. So, the code of the \lstinline!flatMap! -and \lstinline!map! methods \emph{must} wait until all previous values -are computed. We need a new function that can take advantage of the -independence of effects. A possible type signature for that function -is: +The implementations of \lstinline!flatMap! and \lstinline!map! cannot +detect whether the next computations actually depend on any previously +computed values. So, the code of the \lstinline!flatMap! and \lstinline!map! +methods \emph{must} wait until all previous values are computed. We +need a new function that can take advantage of the independence of +effects. A possible type signature for that function is: \begin{lstlisting} def map3(f1: Future[Int], f2: Future[Int], f3: Future[Int], f: (Int, Int, Int) => Int): Future[Int] \end{lstlisting} @@ -484,7 +461,7 @@ \subsection{Data validation with error reporting} \end{lstlisting} We assume that the validation functions will return values of types -\lstinline!F[A]! defined by +\lstinline!F[A]! defined by: \begin{wrapfigure}{l}{0.5\columnwidth}% \vspace{-0.85\baselineskip} @@ -581,7 +558,7 @@ \subsection{Implementing the functions \texttt{map2}, \texttt{map3}, etc. The case head :: tail => zipE(head, zipN(tail)).map { case (h, t) => h :: t } } \end{lstlisting} -The corresponding function \lstinline!mapN! is then defined by +The corresponding function \lstinline!mapN! is then defined by: \begin{lstlisting} def mapN[A, Z](xs: List[Either[String, A]])(f: List[A] => Z): Either[String, Z] = zipN(xs).map(f) \end{lstlisting} @@ -601,24 +578,14 @@ \subsection{Implementing the functions \texttt{map2}, \texttt{map3}, etc. The To compare \lstinline!map! and \lstinline!map2! functions more easily, consider their curried versions \lstinline!fmap! and \lstinline!fmap2!: - -\begin{wrapfigure}{l}{0.543\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} def fmap[A,B](f: A => B): L[A] => L[B] def fmap2[A,B,C](f: A => B => C): L[A] => L[B] => L[C] \end{lstlisting} - -\vspace{-1.45\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.75\baselineskip} \begin{align*} & \text{fmap}:\left(A\rightarrow B\right)\rightarrow L^{A}\rightarrow L^{B}\quad,\\ & \text{fmap}_{2}:\left(A\rightarrow B\rightarrow C\right)\rightarrow L^{A}\rightarrow L^{B}\rightarrow L^{C}\quad. \end{align*} -\vspace{-1.7\baselineskip} - If we try implementing \lstinline!fmap2! via \lstinline!map!, we get stuck: \begin{lstlisting} @@ -628,17 +595,9 @@ \subsection{Implementing the functions \texttt{map2}, \texttt{map3}, etc. The of the required type \lstinline!L[B] => L[C]!. So, we need a function that converts between those types. The new function is called \lstinline!ap! and has the type signature: - -\begin{wrapfigure}{l}{0.543\columnwidth}% -\vspace{-0.6\baselineskip} \begin{lstlisting} def ap[B, C](lf: L[B => C]): L[B] => L[C] \end{lstlisting} - -\vspace{-0.5\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.5\baselineskip} \[ \text{ap}_{L}:L^{A\rightarrow B}\rightarrow L^{A}\rightarrow L^{B}\quad. \] @@ -654,7 +613,10 @@ \subsection{Implementing the functions \texttt{map2}, \texttt{map3}, etc. The \text{fmap}_{2}(f)\triangleq f^{\uparrow L}\bef\text{ap}_{L}\quad. \] Taking into account the curried type signature of \lstinline!fmap2!, -we may rewrite this as \lstinline!fmap2(f)(la)(lb) == ap(la.map(f))(lb)!. +we may rewrite this as: +\begin{lstlisting} +fmap2(f)(la)(lb) == ap(la.map(f))(lb) +\end{lstlisting} This expression is made clearer if we implement infix syntax for \lstinline!fmap! and \lstinline!ap!: \begin{lstlisting} @@ -771,11 +733,11 @@ \subsection{Single-traversal \texttt{fold} operations. I. Applicative \textquote Another example is the computation of word distribution statistics in a large text corpus.\index{corpus}\footnote{Such as the \textsf{``}Common Crawl\textsf{''} corpus, see \texttt{\href{https://commoncrawl.org/}{https://commoncrawl.org/}}} -A \textsf{``}corpus\textsf{''} is a sequence of large chunks of text. Each chunk takes -a long time to download, and storing the entire sequence in memory -is impossible. It is necessary to minimize the number of traversals -of the corpus. Ideally, all computations need to be implemented in -a single traversal. +For our purposes, a \textbf{corpus} is a sequence of large chunks +of text. Each chunk takes a long time to download, and storing the +entire sequence in memory is impossible. It is necessary to minimize +the number of traversals of the corpus. Ideally, all computations +need to be implemented in a single traversal. Note that the \lstinline!sum! and \lstinline!size! methods are particular cases of a \lstinline!foldLeft! operation. We could avoid a double @@ -1099,26 +1061,19 @@ \subsection{Single-traversal \texttt{fold} operations. II. Monadic \textquotedbl the updater function has type \lstinline!(R, Z) => M[R]!.} It is interesting to note that the monadic fold fusion is compatible with the applicative fold fusion. For instance, one could define \lstinline!average! equivalently as: - -\begin{wrapfigure}{l}{0.28\columnwidth}% -\vspace{-1\baselineskip} \begin{lstlisting} val average = for { acc <- sum n <- length } yield acc / n \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent This equivalence comes from the fact that the case class -\lstinline!FoldOp[Z, R, A]! depends on \lstinline!A! only through -the \lstinline!transform! value, which is of type \lstinline!R => A!. -So, the monadic behavior of \lstinline!FoldOp[Z, R, A]! with respect -to \lstinline!A! is similar to that of the \lstinline!Reader! monad -of type \lstinline!R => A!. Similarly to the \lstinline!Reader! -monad, the effects in the \lstinline!FoldOp! monad are independent. +This equivalence comes from the fact that the case class \lstinline!FoldOp[Z, R, A]! +depends on \lstinline!A! only through the \lstinline!transform! +value, which is of type \lstinline!R => A!. So, the monadic behavior +of \lstinline!FoldOp[Z, R, A]! with respect to \lstinline!A! is +similar to that of the \lstinline!Reader! monad of type \lstinline!R => A!. +Similarly to the \lstinline!Reader! monad, the effects in the \lstinline!FoldOp! +monad are independent. \subsection{Parsing with applicative and monadic combinators\label{subsec:Parsing-with-applicative-and-monadic-parsers}} @@ -1349,10 +1304,6 @@ \subsection{Functor block syntax for applicative functors} in a single functor block. This can be done by using \lstinline!zip!, \lstinline!zipLeft!, and \lstinline!zipRight! within source lines, as shown in the previous section. - -\begin{wrapfigure}{l}{0.3\columnwidth}% -\vspace{-0.95\baselineskip} - \begin{lstlisting} for { x <- p @@ -1360,15 +1311,12 @@ \subsection{Functor block syntax for applicative functors} t <- s(x, y, z) ... \end{lstlisting} -\vspace{-0.95\baselineskip} -\end{wrapfigure}% - -\noindent In the code shown at left, \lstinline!q! and \lstinline!r! -cannot depend on each other\textsf{'}s returned values (\lstinline!y! and -\lstinline!z!). However, \lstinline!q! and \lstinline!r! may depend -on any of the previously computed values (such as \lstinline!x!). -Later computations (such as \lstinline!s!) may also depend on the -previous values (\lstinline!x!, \lstinline!y!, \lstinline!z!). +In the code shown above, \lstinline!q! and \lstinline!r! cannot +depend on each other\textsf{'}s returned values (\lstinline!y! and \lstinline!z!). +However, \lstinline!q! and \lstinline!r! may depend on any of the +previously computed values (such as \lstinline!x!). Later computations +(such as \lstinline!s!) may also depend on the previous values (\lstinline!x!, +\lstinline!y!, \lstinline!z!). There have been several proposals\footnote{See, for example, \texttt{\href{https://contributors.scala-lang.org/t/4474}{https://contributors.scala-lang.org/t/4474}}} to add a special block syntax for applicative functors in Scala. So @@ -1545,22 +1493,12 @@ \subsubsection{Statement \label{subsec:Statement-map2-zip-equivalence}\ref{subse signature of \lstinline!ap! is curried, it is more convenient to use the curried version (\lstinline!fmap2!) instead of \lstinline!map2!. Let us write the relationship between \lstinline!ap! and \lstinline!fmap2! -in the code notation:% -\begin{comment} -precarious formatting -\end{comment} - -\begin{wrapfigure}{i}{0.34\columnwidth}% -\vspace{-1.7\baselineskip} +in the code notation: \[ \xymatrix{\xyScaleY{0.4pc}\xyScaleX{2pc} & F^{B\rightarrow C}\ar[rd]\sp(0.5){\text{ap}^{B,C}}\\ F^{A}\ar[ru]\sp(0.45){f^{\uparrow L}}\ar[rr]\sb(0.43){\text{fmap}_{2}\,(f^{:A\rightarrow B\rightarrow C})} & & \left(F^{B}\rightarrow F^{C}\right) } \] -\vspace{-2\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.8\baselineskip} \begin{align*} & \text{fmap}_{2}\,(f^{:A\rightarrow B\rightarrow C})=f^{\uparrow L}\bef\text{ap}^{B,C}\quad,\\ & \text{ap}^{A,B}=\text{fmap}_{2}\,(\text{id}^{:\left(A\rightarrow B\right)\rightarrow A\rightarrow B})\quad. @@ -1701,7 +1639,7 @@ \subsubsection{Statement \label{subsec:Statement-zip-ap-equivalence}\ref{subsec: {\color{greenunder}\text{naturality law of }\text{zip}:}\quad & \quad=(r\times p)\triangleright\text{zip}\bef\big((x\rightarrow x\bef f)\boxtimes\text{id}\big)^{\uparrow L}\bef\text{eval}^{\uparrow L}\quad. \end{align*} The difference between the two sides of the law will disappear if -we show that +we show that: \[ \text{eval}\bef f\overset{?}{=}\big((x\rightarrow x\bef f)\boxtimes\text{id}\big)\bef\text{eval}\quad. \] @@ -1730,7 +1668,7 @@ \subsubsection{Statement \label{subsec:Statement-zip-ap-equivalence}\ref{subsec: {\color{greenunder}\text{composition under }^{\uparrow L}:}\quad & =\text{ap}\big(r\triangleright(\text{pair}\bef(x\rightarrow x\bef\text{eval}))^{\uparrow L}\big)(p)\quad. \end{align*} The remaining difference between $\text{ap}^{\prime}(r)(p)$ and $\text{ap}\,(r)(p)$ -will disappear if we show that +will disappear if we show that: \[ \text{pair}^{:\left(A\rightarrow B\right)\rightarrow A\rightarrow\left(A\rightarrow B\right)\times A}\bef(x^{:A\rightarrow\left(A\rightarrow B\right)\times A}\rightarrow x\bef\text{eval})\overset{?}{=}\text{id}\quad. \] @@ -2037,7 +1975,7 @@ \subsection{Deriving the laws of \texttt{zip} from the laws of \texttt{map2}\lab \[ \text{wu}:L^{\bbnum 1}\quad,\quad\quad\text{pu}_{L}(a)=\text{wu}\triangleright(1\rightarrow a)^{\uparrow L}\quad. \] -We obtain: +Then we get: \begin{align*} {\color{greenunder}\text{left-hand side}:}\quad & \text{zip}\,(\text{pu}_{L}(a)\times q)=\text{zip}\big((\text{wu}\triangleright(1\rightarrow a)^{\uparrow L})\times q\big)\\ {\color{greenunder}\text{naturality law of }\text{zip}:}\quad & \quad=\text{zip}\,(\text{wu}\times q)\triangleright(1\times b^{:B}\rightarrow a\times b)^{\uparrow L}\\ @@ -2060,7 +1998,7 @@ \subsection{Deriving the laws of \texttt{zip} from the laws of \texttt{map2}\lab with an arbitrary value $a^{:A}$ of arbitrary type $A$. Is it correct to simplify the law by discarding that function? To show that it is, we first set $A\triangleq\bbnum 1$ (since the type $A$ may be chosen -arbitrarily) and obtain the function +arbitrarily) and obtain the function: \[ (1\times b^{:B}\rightarrow1\times b)^{\uparrow L}=\text{id}^{\uparrow L}=\text{id}^{:L^{\bbnum 1\times B}\rightarrow L^{\bbnum 1\times B}}\quad. \] @@ -2576,6 +2514,7 @@ \subsubsection{Statement \label{subsec:Statement-co-product-with-constant-functo and the function $\text{zip}_{L}$ reduces to the operation $\oplus$, for which the associativity law holds by assumption.% \begin{comment} +{*}{*}{*} do we need this? \begin{align*} & \text{zip}_{L}\big(p\times\text{zip}_{L}(q\times r)\big)\triangleright\varepsilon_{1,23}^{\uparrow L}=\big((z_{1}\oplus z_{2}\oplus z_{3})+\bbnum 0\big)\triangleright\varepsilon_{1,23}^{\uparrow L}=(z_{1}\oplus z_{2}\oplus z_{3})+\bbnum 0\quad,\\ & \text{zip}_{L}\big(\text{zip}_{L}(p\times q)\times r\big)\triangleright\varepsilon_{12,3}^{\uparrow L}=\big((z_{1}\oplus z_{2}\oplus z_{3})+\bbnum 0\big)\triangleright\varepsilon_{12,3}^{\uparrow L}=(z_{1}\oplus z_{2}\oplus z_{3})+\bbnum 0\quad. @@ -3019,7 +2958,7 @@ \subsubsection{Statement \label{subsec:Statement-polynomial-functor-applicative} and $F^{A}+A\times G^{A}\cong L^{A}$ is applicative. \textbf{(c)} The polynomial functor $S^{\bullet,\bullet}$ can be -expressed as +expressed as: \[ S^{A,R}=P_{0}^{A}+R^{A}\times(P_{1}^{A}+R^{A}\times(...\times(P_{n-1}^{A}+R^{A}\times P_{n}^{A})...))\quad, \] @@ -3057,7 +2996,7 @@ \subsubsection{Statement \label{subsec:Statement-polynomial-functor-applicative} is an arbitrary contrafunctor. The lawful monad gives up to two definitions of a \lstinline!zip! method for the functors $L$ of this type. However, commutativity is not guaranteed for arbitrary $H^{\bullet}$. One -can implement a \lstinline!zip! method with type signature +can implement a \lstinline!zip! method with type signature: \[ \text{zip}:(H^{A}\rightarrow A)\times(H^{B}\rightarrow B)\rightarrow H^{A\times B}\rightarrow A\times B\quad, \] @@ -3866,7 +3805,7 @@ \subsubsection{Statement \label{subsec:Statement-applicative-contrafunctor-expon f^{\downarrow P}=p^{:H^{A}\rightarrow G^{A}}\rightarrow f^{\uparrow H}\bef p\bef f^{\downarrow G}\quad. \] -To verify the left identity law, we need to show that +To verify the left identity law, we need to show that: \[ h^{:H^{A}}\triangleright\big(\text{zip}_{P}(\text{wu}_{P}\times p^{:H^{A}\rightarrow G^{A}})\triangleright\text{ilu}^{\downarrow P}\big)\overset{?}{=}h\triangleright p=p(h)\quad. \] @@ -3930,10 +3869,7 @@ \subsection{Applicative profunctors: Laws and constructions} all fully parametric type constructors are profunctors. An example of a type constructor that \emph{cannot} be a profunctor -is an \textsf{``}unfunctor\index{unfunctor}\textsf{''}: - -\begin{wrapfigure}{l}{0.47\columnwidth}% -\vspace{-1\baselineskip} +is a \textsf{``}GADT\index{GADT}\textsf{''}: \begin{lstlisting} sealed trait U[A] // Unfunctor. @@ -3941,20 +3877,14 @@ \subsection{Applicative profunctors: Laws and constructions} final case class U2(b: String) extends U[Int] final case class U3(b: String) extends U[Long] \end{lstlisting} - -\vspace{0.2\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.8\baselineskip} \[ U^{A}\triangleq\text{Double}^{:U^{\bbnum 1}}+\text{String}^{:U^{\text{Int}}}+\text{String}^{:U^{\text{Long}}}\quad. \] - -\noindent The definition of this type constructor is \emph{not} fully -parametric: values of type $U^{A}$ cannot be created for arbitrary -type parameter $A$ (only for $A=\text{Int}$ or $A=\text{Long}$ -or $A=\bbnum 1$). This prevents us from implementing any \lstinline!map!-like -methods for $U^{\bullet}$. +The definition of this type constructor is \emph{not} fully parametric: +values of type $U^{A}$ cannot be created for arbitrary type parameter +$A$ (only for $A=\text{Int}$ or $A=\text{Long}$ or $A=\bbnum 1$). +This prevents us from implementing any \lstinline!map!-like methods +for $U^{\bullet}$. Profunctors have an \lstinline!xmap! method instead of a \lstinline!map! method: @@ -4008,7 +3938,7 @@ \subsubsection{Definition \label{subsec:Definition-applicative-profunctor}\ref{s with the type signature of \lstinline!pure!. The following example illustrates this (see also Exercise~\ref{subsec:Exercise-profunctor-pure-not-equivalent-1}). -\subsubsection{Example \label{subsec:Example-profunctor-pure-not-equivalent}\ref{subsec:Example-profunctor-pure-not-equivalent}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-profunctor-pure-not-equivalent}\ref{subsec:Example-profunctor-pure-not-equivalent}\index{examples (with code)}} For the profunctor $P^{A}\triangleq\left(A\rightarrow A\right)\rightarrow A$, show that the type $P^{\bbnum 1}$ is \emph{not} equivalent to the @@ -4056,7 +3986,7 @@ \subsubsection{Statement \label{subsec:Statement-applicative-profunctor-composit If $F^{\bullet}$ is an applicative \emph{functor} and $G^{\bullet}$ is an applicative profunctor then the profunctor $P^{A}\triangleq F^{G^{A}}$ (equivalently written as $P\triangleq F\circ G$) is applicative. -In addition, if $G$ is commutative then so is $P$. +If both $F$ and $G$ are commutative then so is $P$. \subparagraph{Proof} @@ -4663,9 +4593,6 @@ \subsection{Equivalence of typeclass methods with laws} There is only one value \lstinline!wu!, but there are many possible functions $p$ of type $\forall A.\,A\rightarrow A$. For instance, we could define a function $p$ like this: - -\begin{wrapfigure}{l}{0.295\columnwidth}% -\vspace{-1\baselineskip} \begin{lstlisting} def p[A]: A => A = { case a: Int => (a + 123) @@ -4673,21 +4600,17 @@ \subsection{Equivalence of typeclass methods with laws} case a => a } \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent This function is defined for all types $A$, so it fits -the type $\forall A.\,A\rightarrow A$. The set of all functions of -type $\forall A.\,A\rightarrow A$ contains a large number of similarly -defined functions. For example, we may replace \lstinline!123! by -another number or select another type instead of \lstinline!Int!. -However, all those functions fail to satisfy the naturality law, which -has the form $f^{:A\rightarrow B}\bef p^{B}=p^{A}\bef f$. The only -function $p$ that satisfies this naturality law is the identity function, -$p^{A}=\text{id}^{A}\triangleq a^{:A}\rightarrow a$ (see Exercise~\ref{subsec:Exercise-hof-composition-1}). -We find that imposing the naturality law removes all functions $p$ -except one ($p=\text{id}$) from the set of functions $p:\text{\ensuremath{\forall A.\,A\rightarrow A}}$. +This function is defined for all types $A$, so it fits the type $\forall A.\,A\rightarrow A$. +The set of all functions of type $\forall A.\,A\rightarrow A$ contains +a large number of similarly defined functions. For example, we may +replace \lstinline!123! by another number or select another type +instead of \lstinline!Int!. However, all those functions fail to +satisfy the naturality law, which has the form $f^{:A\rightarrow B}\bef p^{B}=p^{A}\bef f$. +The only function $p$ that satisfies this naturality law is the identity +function, $p^{A}=\text{id}^{A}\triangleq a^{:A}\rightarrow a$ (see +Exercise~\ref{subsec:Exercise-hof-composition-1}). We find that +imposing the naturality law removes all functions $p$ except one +($p=\text{id}$) from the set of functions $p:\text{\ensuremath{\forall A.\,A\rightarrow A}}$. There remains a set containing a single element ($\text{id}$), which is in a one-to-one correspondence with the set of values of type $\bbnum 1$. @@ -5068,7 +4991,7 @@ \subsubsection{Statement \label{subsec:Statement-ap-functor-laws}\ref{subsec:Sta (see Exercise~\ref{subsec:Exercise-additional-law-of-ap}). \subsection{The pattern of \textquotedblleft functorial\textquotedblright{} typeclasses -in view of category theory\label{subsec:The-pattern-of-functorial-typeclasses}} +and category theory\label{subsec:The-pattern-of-functorial-typeclasses}} In the previous chapters, we have derived several equivalent formulations of the laws of various typeclasses (such as functor, contrafunctor, @@ -5085,7 +5008,7 @@ \subsection{The pattern of \textquotedblleft functorial\textquotedblright{} type method whose type signature looks like a \textsf{``}lifting\textsf{''} between functions of one sort and functions of another sort. For instance, the \lstinline!Filterable! typeclass has the \lstinline!liftOpt! method (Section~\ref{subsec:Motivation-and-laws-for-liftopt-and-equivalence}) -with type signature +with type signature: \[ \text{liftOpt}:\left(A\rightarrow\bbnum 1+B\right)\rightarrow(F^{A}\rightarrow F^{B})\quad. \] @@ -5209,6 +5132,23 @@ \subsubsection{Exercise \label{subsec:Exercise-applicative-II-4-2}\ref{subsec:Ex is a lawful commutative applicative functor. Use applicative functor constructions to avoid long proofs. +\subsubsection{Exercise \label{subsec:Exercise-applicative-I-1-1-1}\ref{subsec:Exercise-applicative-I-1-1-1}} + +Implement a \textsf{``}padding\textsf{''} \lstinline!zip! method for lists \lstinline!List[A]! +such that the shorter list is padded with the \emph{last} value until +its length is equal to that of the longer list. Zipping with an empty +list returns again an empty list. A sample test: +\begin{lstlisting} +def paddingZip[A, B](left: List[A], right: List[B]): List[(A, B)] = ??? + +scala> paddingZip(List(1, 2), List("a", "b", "c", "d")) +res0: List[(Int, String)] = List((1, "a"), (2, "b"), (2, "c"), (2, "d")) + +scala> paddingZip(List(), List(1, 2, 3)) +res1: List[(Unit, Int)] = List() +\end{lstlisting} +Prove that the laws of \lstinline!zip! hold for this function. + \subsubsection{Exercise \label{subsec:Exercise-applicative-II-4}\ref{subsec:Exercise-applicative-II-4}} Show that $F^{A}\triangleq G^{A}+H^{G^{A}}$ is a lawful applicative @@ -5278,7 +5218,7 @@ \subsubsection{Exercise \label{subsec:Exercise-applicative-II-4-1}\ref{subsec:Ex \subsubsection{Exercise \label{subsec:Exercise-applicative-II-5}\ref{subsec:Exercise-applicative-II-5}} -Explicitly prove the laws in contrafunctor product construction (Statement~\ref{subsec:Statement-applicative-contrafunctor-product}). +Explicitly prove the laws in the construction given in Statement~\ref{subsec:Statement-applicative-contrafunctor-product}. \subsubsection{Exercise \label{subsec:Exercise-applicative-II-7}\ref{subsec:Exercise-applicative-II-7}} @@ -5293,7 +5233,7 @@ \subsubsection{Exercise \label{subsec:Exercise-applicative-profunctor-compositio \subsubsection{Exercise \label{subsec:Exercise-profunctor-example}\ref{subsec:Exercise-profunctor-example}} -The type constructor $Q^{\bullet}$ is defined by +The type constructor $Q^{\bullet}$ is defined by: \[ Q^{A}\triangleq\left(A\rightarrow\text{Int}\right)\times A\times\left(A\rightarrow A\right)\quad. \] diff --git a/sofp-src/sofp-cover-page-no-bg.tex b/sofp-src/sofp-cover-page-no-bg.tex index 8aae8133d..af2459807 100644 --- a/sofp-src/sofp-cover-page-no-bg.tex +++ b/sofp-src/sofp-cover-page-no-bg.tex @@ -11,6 +11,8 @@ \vspace{0.4in} \centerline{\fontsize{25pt}{25pt}\selectfont{\textit{A tutorial, with examples in Scala}}} +% End of title. + \begin{textblock*}{8cm}(11.5cm,21cm) \fontsize{18pt}{18pt}\selectfont{\textsf{Sergei Winitzki}} \end{textblock*} diff --git a/sofp-src/sofp-curry-howard.lyx b/sofp-src/sofp-curry-howard.lyx index 2aa408a2b..e8abd50e2 100644 --- a/sofp-src/sofp-curry-howard.lyx +++ b/sofp-src/sofp-curry-howard.lyx @@ -25,7 +25,7 @@ \renewcommand*{\partpagestyle}{empty} % Use a special "equal by definition" symbol. -\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} @@ -178,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -249,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -372,26 +384,18 @@ Values computed by fully parametric functions \end_layout \begin_layout Subsection -Motivation -\end_layout - -\begin_layout Standard -Consider the following sketch of a fully parametric function's Scala code: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "54col%" -status open +Motivation and outlook +\begin_inset CommandInset label +LatexCommand label +name "subsec:Motivation-and-outlook" -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% \end_inset +\end_layout + +\begin_layout Standard +Consider the following sketch of a fully parametric function's Scala code: \begin_inset listings inline false status open @@ -411,28 +415,16 @@ def f[A, B, ...]: ... \begin_layout Plain Layout ... - } -\end_layout - -\end_inset - - + \end_layout \begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - +} \end_layout \end_inset - -\end_layout - -\begin_layout Standard -\noindent If this program compiles without type errors, it means that the types match and, in particular, that the function \begin_inset listings @@ -491,7 +483,7 @@ fmap \end_inset - shown in Section + shown in Example \begin_inset space ~ \end_inset @@ -518,21 +510,6 @@ A \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "54col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -550,28 +527,15 @@ def fmap[A, B](f: A => B): Option[A] => Option[B] = { \begin_layout Plain Layout ... - } -\end_layout - -\end_inset - - \end_layout \begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - +} \end_layout \end_inset - -\end_layout - -\begin_layout Standard -\noindent The reason is that no fully parametric code can compute values of type \begin_inset listings inline true @@ -584,7 +548,15 @@ A \end_inset - from scratch without using a previously given value of type + +\begin_inset Quotes eld +\end_inset + +from scratch +\begin_inset Quotes erd +\end_inset + +, that is, without using any previously given value of type \begin_inset listings inline true status open @@ -596,7 +568,8 @@ A \end_inset - and without applying a function that returns values of type + and without applying a previously given function that returns a value of + type \begin_inset listings inline true status open @@ -682,7 +655,7 @@ fmap \end_inset - must perform pattern matching on a value of type + must perform pattern matching on an argument of type \begin_inset listings inline true status open @@ -695,28 +668,13 @@ Option[A] \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "54col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - \begin_inset listings inline false status open \begin_layout Plain Layout -def fmap[A, B](f: A => B): Option[A] => Option[B] = { +def fmap[A, B](f: A => B)(pa: Option[A]): Option[B] = pa match { \end_layout \begin_layout Plain Layout @@ -747,28 +705,15 @@ def fmap[A, B](f: A => B): Option[A] => Option[B] = { \begin_layout Plain Layout ... - } -\end_layout - -\end_inset - - \end_layout \begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - +} \end_layout \end_inset - -\end_layout - -\begin_layout Standard -\noindent Since the case \begin_inset listings inline true @@ -836,7 +781,7 @@ status open \begin_layout Plain Layout -x:A +x: A \end_layout \end_inset @@ -883,10 +828,95 @@ case ... \end_inset ) of a pattern-matching expression. - + In other words, we should be able to implement the following type signature: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def bad[A, B](f: A => B)(pa: Option[A]): A = ??? +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +So, the question +\begin_inset Quotes eld +\end_inset + +can we compute a value of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +A +\end_layout + +\end_inset + + within a fully parametric function with arguments of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +B +\end_layout + +\end_inset + + and +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +C +\end_layout + +\end_inset + + +\begin_inset Quotes erd +\end_inset + + is equivalent to the question +\begin_inset Quotes eld +\end_inset + +can be implement a fully parametric function of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +(B, C) => A +\end_layout + +\end_inset + + +\begin_inset Quotes erd +\end_inset + +. + From now on, we will focus on the latter kind of questions. \end_layout \begin_layout Standard +\begin_inset Note Note +status collapsed + +\begin_layout Plain Layout The body of \begin_inset listings inline true @@ -992,46 +1022,29 @@ A . \end_layout -\begin_layout Standard -Another example where one cannot compute a value of a certain type is the - following code: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "54col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -10baselineskip% \end_inset - +Here are some other examples where +\emph on +no +\emph default + fully parametric code can implement a given type signature: \begin_inset listings inline false status open \begin_layout Plain Layout -def before[A, B, C](f: A => B, g: B => C): A => C = { +def bad2[A, B](f: A => B): A = ??? \end_layout \begin_layout Plain Layout - // val h: C => A = ??? // Cannot compute h here! \end_layout \begin_layout Plain Layout - a => g(f(a)) // Can compute a value of type A => C. -\end_layout - -\begin_layout Plain Layout - -} +def bad3[A, B, C](p: A => Either[B, C]): Either[A => B, A => C] = ??? \end_layout \end_inset @@ -1039,132 +1052,146 @@ def before[A, B, C](f: A => B, g: B => C): A => C = { \end_layout -\begin_layout Plain Layout -\begin_inset VSpace -120baselineskip% -\end_inset +\begin_layout Standard +The problem with +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +bad2 \end_layout \end_inset + is that no data of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +A \end_layout -\begin_layout Standard -\noindent -The body of +\end_inset + + is given, while the given function \begin_inset listings inline true status open \begin_layout Plain Layout -before +f \end_layout \end_inset - may only use the arguments + returns values of type \begin_inset listings inline true status open \begin_layout Plain Layout -f +B \end_layout \end_inset - and +, not \begin_inset listings inline true status open \begin_layout Plain Layout -g +A \end_layout \end_inset . - We can compose + +\end_layout + +\begin_layout Standard +The problem with \begin_inset listings inline true status open \begin_layout Plain Layout -f +bad3 \end_layout \end_inset - and + is that it needs to hard-code the decision of whether to return the \begin_inset listings inline true status open \begin_layout Plain Layout -g +Left \end_layout \end_inset - to get a value of type + or the \begin_inset listings inline true status open \begin_layout Plain Layout -A => C +Right \end_layout \end_inset -. - But it is impossible to compute a value + part of \begin_inset listings inline true status open \begin_layout Plain Layout -h +Either \end_layout \end_inset - of type +. + That decision cannot depend on the function \begin_inset listings inline true status open \begin_layout Plain Layout -C => A +p \end_layout \end_inset -, no matter what code we try to write. - The reason is that the body of + because one cannot pattern-match on a function, and because \begin_inset listings inline true status open \begin_layout Plain Layout -before +bad3 \end_layout \end_inset - has no given values of type + does not receive any data of type \begin_inset listings inline true status open @@ -1176,141 +1203,346 @@ A \end_inset - and no functions that return values of type + and so cannot call \begin_inset listings inline true status open \begin_layout Plain Layout -A +p \end_layout \end_inset -, so a nameless function such as +. + Suppose \begin_inset listings inline true status open \begin_layout Plain Layout -{c:C => ???} +bad3 \end_layout \end_inset - cannot compute a return value of type + is hard-coded to always return a \begin_inset listings inline true status open \begin_layout Plain Layout -A +Left(f) \end_layout \end_inset -. - Since a fully parametric function cannot create values of an arbitrary - type + with some \begin_inset listings inline true status open \begin_layout Plain Layout -A +f: A => B \end_layout \end_inset - from scratch, computing +. + It is then necessary to compute \begin_inset listings inline true status open \begin_layout Plain Layout -h +f \end_layout \end_inset - within the body of + from \begin_inset listings inline true status open \begin_layout Plain Layout -before +p \end_layout \end_inset - seems to be impossible. -\end_layout - -\begin_layout Standard -Can we prove rigorously that a value of type +, but that is impossible: the given function \begin_inset listings inline true status open \begin_layout Plain Layout -C => A +p \end_layout \end_inset - cannot be computed within the body of + may return either \begin_inset listings inline true status open \begin_layout Plain Layout -before +Left(b) \end_layout \end_inset -? Or, perhaps, a clever trick could produce a value of that type? So far, - we only saw informal arguments about whether values of certain types can - be computed. - To make those arguments rigorous, we need to translate statements such - as -\begin_inset Quotes eld -\end_inset - -a fully parametric function + or \begin_inset listings inline true status open \begin_layout Plain Layout -before +Right(c) \end_layout \end_inset - can compute a value of type + for different values of its argument (of type \begin_inset listings inline true status open \begin_layout Plain Layout -C => A +A \end_layout \end_inset - +). + This data is insufficient to create a function of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +A => B +\end_layout + +\end_inset + +. + Similarly, +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +bad3 +\end_layout + +\end_inset + + is not able to return +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Right(f) +\end_layout + +\end_inset + + with some +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +f: A => C +\end_layout + +\end_inset + +. +\end_layout + +\begin_layout Standard +In all these examples, we see that the impossibility of implementing a type + signature means that the information given in a function's arguments is + in some way insufficient for computing the result value. +\end_layout + +\begin_layout Standard +The type signature inverse to that of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +bad3 +\end_layout + +\end_inset + + is: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def good3[A, B, C](q: Either[A => B, A => C]): A => Either[B, C] = ??? +\end_layout + +\end_inset + +This type signature +\emph on +can +\emph default + be implemented, but only in one way: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def good3[A, B, C](q: Either[A => B, A => C]): A => Either[B, C] = q match + { +\end_layout + +\begin_layout Plain Layout + + case Left(k) => { a => Left(k(a)) } +\end_layout + +\begin_layout Plain Layout + + case Right(k) => { a => Right(k(a)) } +\end_layout + +\begin_layout Plain Layout + +} +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +So, when working with fully parametric code and looking at some type signature + of a function, we may ask the question — is that type signature implementable, + and if so, can we derive the code by just +\begin_inset Quotes eld +\end_inset + +following the types +\begin_inset Quotes erd +\end_inset + +? It is remarkable that this question can be asked at all. + When working with non-functional programming languages, the notion of fully + parametric functions is usually not relevant, and implementations cannot + be derived from types. + But in functional programming, fully parametric functions are used often. + It is then important for the programmer to know whether a given fully parametri +c type signature can be implemented, and if so, to be able to derive the + code. +\end_layout + +\begin_layout Standard +Can we prove rigorously that the functions +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +bad +\end_layout + +\end_inset + +, +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +bad2 +\end_layout + +\end_inset + +, +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +bad3 +\end_layout + +\end_inset + + cannot be implemented by any fully parametric code? Or, perhaps, we were + mistaken and a clever trick +\emph on +could +\emph default + produce some code for those type signatures? +\end_layout + +\begin_layout Standard +So far, we only saw informal arguments about whether values of certain types + can be computed. + To make those arguments rigorous, we need to translate statements such + as +\begin_inset Quotes eld +\end_inset + +a fully parametric function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +before +\end_layout + +\end_inset + + can compute a value of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +C => A +\end_layout + +\end_inset + + \begin_inset Quotes erd \end_inset @@ -1407,7 +1639,7 @@ Here \begin_inset Formula $(C\rightarrow D)\rightarrow E$ \end_inset -, built from other type parameters. +, built from some type parameters. \end_layout \begin_layout Standard @@ -1592,8 +1824,8 @@ sequent (in logic)!goal \end_layout \begin_layout Standard -Sequents provide a notation for the questions about what types of values - may be computed by fully parametric functions. +Sequents provide a notation for the questions about types of fully parametric + functions. Since our goal is to answer such questions rigorously, we will need to be able to \emph on @@ -1614,28 +1846,34 @@ noprefix "false" \end_inset ). - The following sequents correspond to some examples we just saw: + The following sequents correspond to the type signatures we just saw: \begin_inset Formula \begin{align*} \text{\texttt{fmap} for \texttt{Option}}:\quad & {\cal CH}(\text{\texttt{A => B}})\vdash{\cal CH}(\text{\texttt{Option[A] => Option[B]}})\\ \text{the function \texttt{before}}:\quad & {\cal CH}(\text{\texttt{A => B}}),{\cal CH}(\text{\texttt{B => C}})\vdash{\cal CH}(\text{\texttt{A => C}})\\ -\text{value of type }A\text{ within \texttt{fmap}}:\quad & {\cal CH}(\text{\texttt{A => B}}),{\cal CH}(\text{\texttt{Option[A]}})\vdash{\cal CH}(\text{\texttt{A}})\\ -\text{value of type }C\rightarrow A\text{ within \texttt{before}}:\quad & {\cal CH}(\text{\texttt{A => B}}),{\cal CH}(\text{\texttt{B => C}})\vdash{\cal CH}(\text{\texttt{C => A}}) +\text{the function }\text{\texttt{bad}}:\quad & {\cal CH}(\text{\texttt{A => B}}),{\cal CH}(\text{\texttt{Option[A]}})\vdash{\cal CH}(\text{\texttt{A}})\\ +\text{the function }\text{\texttt{bad2}}:\quad & {\cal CH}(\text{\texttt{A => B}}),{\cal CH}(\text{\texttt{B => C}})\vdash{\cal CH}(\text{\texttt{A}}) \end{align*} \end_inset -So far, we only gave informal arguments towards proving the first two sequents - and -\emph on -disproving -\emph default - the last two. +So far, we only saw informal arguments towards proving the first two sequents + and disproving the last two. We will now develop tools for proving sequents rigorously. - We will then be able to prove that a given fully parametric function can + +\begin_inset Note Note +status collapsed + +\begin_layout Plain Layout +We will then be able to prove that a given fully parametric function can compute values of a certain type (or that it cannot). \end_layout +\end_inset + + +\end_layout + \begin_layout Standard In formal logic, sequents are proved \begin_inset Index idx @@ -1659,8 +1897,33 @@ logics \end_inset -propositions. - That logic's axioms and derivation rules will give correct answers about - implementable and non-implementable types. + To discover that logic's complete set of axioms and derivation rules, we + need to examine systematically what types and code constructions are possible + in a fully parametric function. + The resulting logic is known under the name +\begin_inset Quotes eld +\end_inset + +constructive logic +\begin_inset Quotes erd +\end_inset + +. + That logic's axioms and derivation rules directly correspond to programming + language constructions allowed by fully parametric code. + For that reason, constructive logic gives correct answers about implementable + and non-implementable type signatures of fully parametric functions. +\end_layout + +\begin_layout Standard +We will then be able to borrow the results and methods already available + in the mathematical literature on formal logic. + The main result is an algorithm (called LJT) for finding a proof for a + given sequent in the constructive logic. + If a proof is found, the algorithm also provides the code of a function + that has the type signature corresponding to the sequent. + If a proof is not found, it means that the given type signature cannot + be implemented by fully parametric code. \end_layout \begin_layout Subsection @@ -1700,13 +1963,23 @@ A proposition \begin_inset Formula ${\cal CH}$ \end_inset --propositions within the body of a -\emph on -chosen -\emph default - fully parametric function, i.e., with a fixed set of premises. - We will then temporarily omit the premises and write just -\begin_inset Formula ${\cal CH}(A)$ +-propositions within sequents of the form: +\begin_inset Formula +\[ +{\cal CH}(X),{\cal CH}(Y),...,{\cal CH}(Z)\vdash{\cal CH}(A) +\] + +\end_inset + +that represent type signatures of fully parametric functions. + It will be convenient to shorten the notation and to denote all premises + by the symbol +\begin_inset Formula $\Gamma$ +\end_inset + +. + We will then write just +\begin_inset Formula $\Gamma\vdash{\cal CH}(A)$ \end_inset instead of @@ -1714,7 +1987,7 @@ chosen \end_inset . - In later sections, we will need to write full sets of premises for sequents. + \end_layout \begin_layout Standard @@ -1739,6 +2012,43 @@ noprefix "false" -propositions for case classes and for disjunctive types. We will now extend this reasoning systematically to all type constructions that fully parametric programs could use. + The result will be that we can rewrite +\begin_inset Formula ${\cal CH}$ +\end_inset + +-propositions with arbitrary type expressions, such as +\begin_inset Formula ${\cal CH}($ +\end_inset + + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Either[(A, A), Option[B] => Either[(A, B), C]] +\end_layout + +\end_inset + + +\begin_inset Formula $)$ +\end_inset + +, in terms of +\begin_inset Formula ${\cal CH}$ +\end_inset + +-propositions for simple type parameters: +\begin_inset Formula ${\cal CH}(A)$ +\end_inset + +, +\begin_inset Formula ${\cal CH}(B)$ +\end_inset + +, etc. A special type notation \begin_inset Index idx status open @@ -2004,21 +2314,6 @@ N1 \end_inset without using any other given values: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "42col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2030,23 +2325,6 @@ val x: N1 = N1() \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent So, the proposition \begin_inset Formula ${\cal CH}($ \end_inset @@ -2220,6 +2498,11 @@ abc ... \end_layout +\begin_layout Plain Layout + +} +\end_layout + \end_inset So, the rule for primitive types is the same as that for the @@ -2484,7 +2767,7 @@ is translated to the type notation as: \end_inset -Here, the type notation is significantly shorter because it omits all case +Here the type notation is significantly shorter because it omits all case class names and part names from the type definitions. Using the type notation, the rule for disjunctive types is written as: \begin_inset Formula @@ -2521,21 +2804,6 @@ A => B . To compute a value of that type, we need to write code like this: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2557,24 +2825,7 @@ val f: A => B = { (a: A) => \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -The inner scope of the function needs to compute a value of type +The inner scope of this function needs to compute a value of type \begin_inset Formula $B$ \end_inset @@ -2749,7 +3000,27 @@ universal quantifier ( universal quantifier \series default in logic. - + We read +\begin_inset Formula $\forall A.\,{\cal CH}(B)$ +\end_inset + + as the proposition +\begin_inset Quotes eld +\end_inset + +for all types +\begin_inset Formula $A$ +\end_inset + +, we can compute a value of type +\begin_inset Formula $B$ +\end_inset + + +\begin_inset Quotes erd +\end_inset + +. \end_layout \begin_layout Standard @@ -2952,6 +3223,38 @@ names The type notation is designed to support nameless type expressions. \end_layout +\begin_layout Standard +The rules just shown will allow us to express +\begin_inset Formula ${\cal CH}$ +\end_inset + +-propositions for complicated types via +\begin_inset Formula ${\cal CH}$ +\end_inset + +-propositions for type parameters. + Then any type signature can be rewritten as a sequent that contains +\begin_inset Formula ${\cal CH}$ +\end_inset + +-propositions only for the individual type parameters. + +\end_layout + +\begin_layout Standard +In this way, we see a correspondence between fully parametric type signatures + and logical sequents that express the proposition +\begin_inset Quotes eld +\end_inset + +the type signature can be implemented +\begin_inset Quotes erd +\end_inset + +. + This is the first half of the Curry-Howard correspondence. +\end_layout + \begin_layout Standard Table \begin_inset space ~ @@ -3114,30 +3417,17 @@ status open \begin_layout Plain Layout \align center \begin_inset Tabular - + - \begin_inset Text \begin_layout Plain Layout -\series bold -\size small -Type construction -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - \series bold \size small Scala syntax @@ -3177,17 +3467,6 @@ Type notation \begin_inset Text -\begin_layout Plain Layout - -\size small -type parameter -\end_layout - -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout \begin_inset listings inline true @@ -3234,19 +3513,6 @@ status open \begin_inset Text -\begin_layout Plain Layout - -\size small -product type (tuple) -\size default - -\end_layout - -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout \begin_inset listings inline true @@ -3301,19 +3567,6 @@ status open \begin_inset Text -\begin_layout Plain Layout - -\size small -disjunctive type -\size default - -\end_layout - -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout \begin_inset listings inline true @@ -3368,19 +3621,6 @@ Either[A, B] \begin_inset Text -\begin_layout Plain Layout - -\size small -function type -\size default - -\end_layout - -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout \begin_inset listings inline true @@ -3435,25 +3675,6 @@ A => B \begin_inset Text -\begin_layout Plain Layout - -\size small -unit or a -\begin_inset Quotes eld -\end_inset - -named unit -\begin_inset Quotes erd -\end_inset - - type -\end_layout - -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout \begin_inset listings inline true @@ -3502,17 +3723,6 @@ Unit \begin_layout Plain Layout -\size small -primitive type -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - \size small \begin_inset listings inline true @@ -3577,17 +3787,6 @@ String \begin_inset Text -\begin_layout Plain Layout - -\size small -void type -\end_layout - -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout \begin_inset listings inline true @@ -3634,17 +3833,6 @@ Nothing \begin_inset Text -\begin_layout Plain Layout - -\size small -value parameterized by type -\end_layout - -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout \begin_inset listings inline true @@ -3691,17 +3879,6 @@ def f[A]: F[A] \begin_inset Text -\begin_layout Plain Layout - -\size small -type with quantifier -\end_layout - -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout \begin_inset listings inline true @@ -3767,7 +3944,7 @@ type notation \end_inset - between type constructions and + between types and \begin_inset Formula ${\cal CH}$ \end_inset @@ -3792,12 +3969,12 @@ name "tab:ch-correspondence-type-notation-CH-propositions" \end_layout \begin_layout Subsection -Solved examples: Type notation +Examples: Type notation \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -3809,14 +3986,11 @@ solved examples From now on, we will prefer to write types in the type notation rather than in the Scala syntax. The type notation allows us to write nameless type expressions and makes - the structure of complicated types more clear, compared with the Scala - syntax. + the structure of complicated types clearer than in the Scala syntax. Names of types and parts of types are, of course, helpful for reminding programmers of the meaning of data in a program. - However, writing names for every part of every type is not helpful for - reasoning about the properties of types. - Type notation makes reasoning about types easier, as we will see throughout - this chapter. + However, writing names for every part of every type does not help reasoning + about the properties of types. Once the programmer has finished deriving the necessary types and verifying their properties, the type notation can be straightforwardly translated into Scala code. @@ -3943,7 +4117,7 @@ status open \begin_layout Plain Layout -x:A +x: A \end_layout \end_inset @@ -5108,7 +5282,7 @@ Q[T, A] \end_inset - corresponding to the type notation: + corresponding to this type notation: \begin_inset Formula \[ Q^{T,A}\triangleq\bbnum 1+T\times A+\text{Int}\times(T\rightarrow T)+\text{String}\times A\quad. @@ -5141,7 +5315,7 @@ noprefix "false" \end_layout \begin_layout Standard -Rewrite +Convert the type \begin_inset listings inline true status open @@ -5153,7 +5327,7 @@ Either[(A, Int), Either[(A, Char), (A, Float)]] \end_inset - in the type notation. + from Scala syntax to the type notation. \end_layout @@ -5710,12 +5884,15 @@ name "subsec:The-rules-of-proof" \begin_layout Standard To derive the suitable logical axioms and proof rules systematically, let - us examine what makes a + us examine what could make a sequent with \begin_inset Formula ${\cal CH}$ \end_inset --proposition true. - A sequent +-propositions true. +\end_layout + +\begin_layout Standard +A sequent \begin_inset Formula ${\cal CH}(A)\vdash{\cal CH}(X)$ \end_inset @@ -5911,7 +6088,7 @@ def f[A, B, ...](a: A, b: B): X = { // Any given type signature. \begin_layout Plain Layout } // 9) Call f() itself recursively. - Not included here because not supported by CH-propositions. + Not included here because recursion is not supported by CH-propositions. \end_layout \end_inset @@ -5989,10 +6166,10 @@ fully parametric code \begin_inset Index idx -status collapsed +status open \begin_layout Plain Layout -fully parametric!code +fully parametric!code|textit \end_layout \end_inset @@ -6056,7 +6233,17 @@ eight code constructions \end_layout \begin_layout Standard -In the following text, we will need to use the full formulation +In this way, we will get a correspondence between proofs of sequents and + fully parametric programs. + This is the second half of the Curry-Howard correspondence. +\end_layout + +\begin_layout Standard +In the following text, we will need to write +\begin_inset Formula ${\cal CH}$ +\end_inset + +-propositions such as Eq. \begin_inset space ~ \end_inset @@ -6070,11 +6257,7 @@ noprefix "false" \end_inset -) of -\begin_inset Formula ${\cal CH}$ -\end_inset - --propositions and write them as sequents such as Eq. +) as sequents such as Eq. \begin_inset space ~ \end_inset @@ -6089,32 +6272,47 @@ noprefix "false" \end_inset ). - For brevity, we define -\begin_inset Formula $\alpha\triangleq{\cal CH}(A)$ + As we have seen, +\begin_inset Formula ${\cal CH}$ +\end_inset + +-propositions involving complicated type expressions can be always rewritten + via +\begin_inset Formula ${\cal CH}$ +\end_inset + +-propositions for individual type parameters ( +\begin_inset Formula ${\cal CH}(A)$ \end_inset , -\begin_inset Formula $\beta\triangleq{\cal CH}(B)$ +\begin_inset Formula ${\cal CH}(B)$ \end_inset -, etc. - It is also customary to use the letter -\begin_inset Formula $\Gamma$ +, etc.). + So, we will only need sequents involving such +\begin_inset Formula ${\cal CH}$ \end_inset - to denote a set of premises, such as -\begin_inset Formula ${\cal CH}(X)$ +-propositions. + For brevity, we denote +\begin_inset Formula $\alpha\triangleq{\cal CH}(A)$ \end_inset , -\begin_inset Formula ${\cal CH}(Y)$ +\begin_inset Formula $\beta\triangleq{\cal CH}(B)$ \end_inset -, ..., -\begin_inset Formula ${\cal CH}(Z)$ +, etc. + We will use the letter +\begin_inset Formula $\Gamma$ \end_inset - in Eq. + to stand for a set of premises and write a shorter formula +\begin_inset Formula $\Gamma\vdash\alpha$ +\end_inset + + instead of the sequent \begin_inset space ~ \end_inset @@ -6129,26 +6327,7 @@ noprefix "false" \end_inset ). - So, we can write a shorter formula -\begin_inset Formula $\Gamma\vdash\alpha$ -\end_inset - - instead of the sequent -\begin_inset space ~ -\end_inset - -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-sequent" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -). -\end_layout +\end_layout \begin_layout Standard With these notations, we list the rules for proving @@ -7019,7 +7198,7 @@ t._2 The proof code can be written as: \begin_inset Formula \[ -\text{Proof}\left(\Gamma\vdash\alpha\right)=\pi_{1}\left(\text{Proof}\left(\Gamma\vdash\alpha\wedge\beta\right)\right)\quad,\quad\quad\text{Proof}\left(\Gamma\vdash\beta\right)=\pi_{2}\left(\text{Proof}\left(\Gamma\vdash\alpha\wedge\beta\right)\right)\quad, +\text{Proof}\left(\Gamma\vdash\alpha\right)=\pi_{1}\left(\text{Proof}\left(\Gamma\vdash\alpha\wedge\beta\right)\right)\quad,\quad\text{Proof}\left(\Gamma\vdash\beta\right)=\pi_{2}\left(\text{Proof}\left(\Gamma\vdash\alpha\wedge\beta\right)\right)\quad, \] \end_inset @@ -7204,9 +7383,10 @@ Right as: \begin_inset Formula -\[ -\text{Proof}\left(\Gamma\vdash\alpha\vee\beta\right)=\text{Left}\,(\text{Proof}\left(\Gamma\vdash\alpha\right))\quad,\quad\quad\text{Proof}\left(\Gamma\vdash\alpha\vee\beta\right)=\text{Right}\,(\text{Proof}\left(\Gamma\vdash\beta\right))\quad. -\] +\begin{align*} +\text{Proof}\left(\Gamma\vdash\alpha\vee\beta\right) & =\text{Left}\,(\text{Proof}\left(\Gamma\vdash\alpha\right))\quad,\\ +\text{Proof}\left(\Gamma\vdash\alpha\vee\beta\right) & =\text{Right}\,(\text{Proof}\left(\Gamma\vdash\beta\right))\quad. +\end{align*} \end_inset @@ -7231,21 +7411,6 @@ Either[A, B] \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "42col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -7272,23 +7437,6 @@ val result: C = (e: Either[A, B]) match { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -120baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Here, \begin_inset listings inline true @@ -7628,21 +7776,6 @@ name "subsec:Example:-Proving-a-ch-proposition" \begin_layout Standard We will implement a fully parametric function: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -7654,23 +7787,6 @@ def f[A, B]: ((A => A) => B) => B = ??? \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Implementing this function is the same as being able to compute a value of type \begin_inset Formula $F$ @@ -8067,7 +8183,7 @@ has_inner_box 1 inner_pos "t" use_parbox 0 use_makebox 0 -width "50col%" +width "70col%" special "none" height "1in" height_special "totalheight" @@ -8546,10 +8662,8 @@ noprefix "false" \end_inset below). - That algorithm first translates a type signature containing tuples, disjunctive - types, and function types into a logical formula. - Then the algorithm either finds that the given formula cannot be proved, - or it finds a proof and infers code that has the given type signature. + That algorithm either finds that the given formula cannot be proved, or + it finds a proof and infers code that has the given type signature. \end_layout \begin_layout Standard @@ -8904,7 +9018,7 @@ As another example, let us verify that the type signature from Section \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Example:-Failure-of-Boolean-logic" +reference "subsec:Motivation-and-outlook" plural "false" caps "false" noprefix "false" @@ -8918,7 +9032,8 @@ status open \begin_layout Plain Layout -@ def bad[A, B, C](g: A => Either[B, C]): Either[A => B, A => C] = implement +@ def bad2[A, B, C](g: A => Either[B, C]): Either[A => B, A => C] = + implement \end_layout \begin_layout Plain Layout @@ -8972,14 +9087,10 @@ guided by the types \end_layout \begin_layout Subsection -Failure of Boolean logic in reasoning about -\begin_inset Formula $\mathcal{CH}$ -\end_inset - --propositions +The LJT algorithm \begin_inset CommandInset label LatexCommand label -name "subsec:Example:-Failure-of-Boolean-logic" +name "app:The-LJT-algorithm" \end_inset @@ -8987,1046 +9098,1309 @@ name "subsec:Example:-Failure-of-Boolean-logic" \end_layout \begin_layout Standard -Programmers are most familiar with the -\begin_inset Index idx -status open +In the previous section, we saw an example showing that the rules in Table +\begin_inset space ~ +\end_inset -\begin_layout Plain Layout -Boolean logic + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Proof-rules-of-constructive-and-boolean" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + do not provide an algorithm for finding a proof for a given sequent. \end_layout +\begin_layout Standard +To illustrate this problem on another example, let us try proving the sequent: +\begin_inset Formula +\[ +A,B\vee C\vdash(A\wedge B)\vee C\quad. +\] + \end_inset -Boolean logic whose operations are written in Scala as +We expect that this sequent is provable because we can write the corresponding + Scala code: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -x && y +def f[A, B, C](a: A): Either[B, C] => Either[(A, B), C] = { \end_layout -\end_inset +\begin_layout Plain Layout - (conjunction), -\begin_inset listings -inline true -status open + case Left(b) => Left((a, b)) +\end_layout \begin_layout Plain Layout -x || y + case Right(c) => Right(c) +\end_layout + +\begin_layout Plain Layout + +} \end_layout \end_inset - (disjunction), and +How can we obtain a proof of this sequent according to the rules in Table +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Proof-rules-of-constructive-and-boolean" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +? We find that we could potentially apply the rules +\begin_inset Quotes eld +\end_inset + +create \begin_inset listings inline true status open \begin_layout Plain Layout -!x +Left \end_layout \end_inset - (negation). - However, it turns out that the Boolean logic does -\emph on -not -\emph default - always produce correct conclusions when reasoning about -\begin_inset Formula ${\cal CH}$ -\end_inset - --propositions. - One needs to use the constructive logic to reason correctly about implementable - type signatures. -\end_layout -\begin_layout Standard -Let us nevertheless briefly look at how Boolean logic would handle that - reasoning. - In the Boolean logic, each proposition ( -\begin_inset Formula $\alpha$ +\begin_inset Quotes erd \end_inset , -\begin_inset Formula $\beta$ +\begin_inset Quotes eld \end_inset -, ...) is either -\begin_inset Formula $True$ -\end_inset +create +\begin_inset listings +inline true +status open - or -\begin_inset Formula $False$ -\end_inset +\begin_layout Plain Layout + +Right +\end_layout -. - The operations are -\begin_inset Formula $\alpha\wedge\beta$ \end_inset - (conjunction), -\begin_inset Formula $\alpha\vee\beta$ + +\begin_inset Quotes erd \end_inset - (disjunction), and -\begin_inset Formula $\neg\alpha$ +, +\begin_inset Quotes eld \end_inset - (negation). - The -\series bold -implication -\series default - -\begin_inset Index idx +use +\begin_inset listings +inline true status open \begin_layout Plain Layout -implication (in logic) + +Either \end_layout \end_inset - ( -\begin_inset Formula $\Rightarrow$ -\end_inset -) is defined through other operations by: -\begin_inset Formula -\begin{equation} -\left(\alpha\Rightarrow\beta\right)\triangleq\left((\neg\alpha)\vee\beta\right)\quad.\label{eq:ch-definition-of-implication-in-Boolean-logic} -\end{equation} +\begin_inset Quotes erd +\end_inset +, and +\begin_inset Quotes eld \end_inset +use function +\begin_inset Quotes erd +\end_inset +. + However, no matter what rule we choose, we will get stuck at the next step. + Let us see why: \end_layout \begin_layout Standard -To verify whether a formula is true in the Boolean logic, we can substitute - either -\begin_inset Formula $True$ -\end_inset - - or -\begin_inset Formula $False$ -\end_inset - - into every variable and check if the formula has the value -\begin_inset Formula $True$ +To apply +\begin_inset Quotes eld \end_inset - in all possible cases. - The result can be arranged into a -\begin_inset Index idx +create +\begin_inset listings +inline true status open \begin_layout Plain Layout -truth table + +Left \end_layout \end_inset -truth table. - The formula is true if all values in its truth table are -\begin_inset Formula $True$ + +\begin_inset Quotes erd +\end_inset + +, we first need to prove the sequent +\begin_inset Formula $A,B\vee C\vdash A\wedge B$ \end_inset . -\end_layout + But this sequent cannot be proved: we do not necessarily have values of + both types +\begin_inset Formula $A$ +\end_inset -\begin_layout Standard -Disjunction, conjunction, negation, and implication operations have the - following truth table: -\end_layout + and +\begin_inset Formula $B$ +\end_inset -\begin_layout Standard -\align center + if we are only given values of type +\begin_inset Formula $A$ +\end_inset -\size small -\begin_inset Tabular - - - - - - - - - - -\begin_inset Text + and of type +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\size small -\begin_inset Formula $\alpha$ -\end_inset - - +Either[B, C] \end_layout \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $\beta$ +. + To apply +\begin_inset Quotes eld \end_inset +create +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +Right \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\series bold -\size small -\begin_inset Formula $\alpha\vee\beta$ +\begin_inset Quotes erd \end_inset +, we need to prove the sequent +\begin_inset Formula $A,B\vee C\vdash C$ +\end_inset -\end_layout - +. + Again, we find that this sequent cannot be proved. + The next choice is the rule +\begin_inset Quotes eld \end_inset - - -\begin_inset Text + +use +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\series bold -\size small -\begin_inset Formula $\alpha\wedge\beta$ +Either +\end_layout + \end_inset -\end_layout +\begin_inset Quotes erd +\end_inset + that matches any goal of the sequent as the proposition +\begin_inset Formula $\gamma$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +. + But we are then required to choose two new propositions ( +\begin_inset Formula $\alpha$ +\end_inset -\series bold -\size small -\begin_inset Formula $\neg\alpha$ + and +\begin_inset Formula $\beta$ \end_inset +) such that we can prove +\begin_inset Formula $A,B\vee C\vdash\alpha\vee\beta$ +\end_inset -\end_layout + as well as +\begin_inset Formula $A,B\vee C,\alpha\vdash(A\wedge B)\vee C$ +\end_inset + and +\begin_inset Formula $A,B\vee C,\beta\vdash(A\wedge B)\vee C$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +. + It is not clear how we should choose +\begin_inset Formula $\alpha$ +\end_inset -\series bold -\size small -\begin_inset Formula $\alpha\Rightarrow\beta$ + and +\begin_inset Formula $\beta$ \end_inset + in order to make progress in the proof. + The remaining rule, +\begin_inset Quotes eld +\end_inset -\end_layout +use function +\begin_inset Quotes erd +\end_inset +, similarly requires us to choose a new proposition +\begin_inset Formula $\alpha$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout + such that we can prove +\begin_inset Formula $A,B\vee C\vdash\alpha$ +\end_inset -\size small -\begin_inset Formula $True$ + and +\begin_inset Formula $A,B\vee C\vdash\alpha\Rightarrow((A\wedge B)\vee C)$ \end_inset +. + The rules give us no guidance for choosing +\begin_inset Formula $\alpha$ +\end_inset + appropriately. \end_layout +\begin_layout Standard +To see that the rules in Table +\begin_inset space ~ \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $True$ -\end_inset +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Proof-rules-for-constructive-logic" +plural "false" +caps "false" +noprefix "false" -\end_layout +\end_inset + are not helpful for proof search, note that the rules +\begin_inset Quotes eld \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +use function +\begin_inset Quotes erd +\end_inset -\size small -\begin_inset Formula $True$ + and +\begin_inset Quotes eld \end_inset +use +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +Either \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\size small -\begin_inset Formula $True$ +\begin_inset Quotes erd \end_inset + require us to choose new unknown propositions and to prove more complicated + sequents than the ones we had before. + For instance, the rule +\begin_inset Quotes eld +\end_inset -\end_layout +use function +\begin_inset Quotes erd +\end_inset + gives a proof of +\begin_inset Formula $\Gamma\vdash\beta$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + only if we first choose some other proposition +\begin_inset Formula $\alpha$ +\end_inset -\size small -\begin_inset Formula $False$ + and prove the sequents +\begin_inset Formula $\Gamma\vdash\alpha$ \end_inset + and +\begin_inset Formula $\Gamma\vdash\alpha\Rightarrow\beta$ +\end_inset -\end_layout +. + The rule does not say how to choose the proposition +\begin_inset Formula $\alpha$ +\end_inset + correctly. + We need to guess the correct +\begin_inset Formula $\alpha$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + by trial and error. + Even after choosing +\begin_inset Formula $\alpha$ +\end_inset -\size small -\begin_inset Formula $True$ + in some way, we will have to prove a more complicated sequent ( +\begin_inset Formula $\Gamma\vdash\alpha\Rightarrow\beta$ \end_inset +). + It is not guaranteed that we are getting closer to finding the proof of + the initial sequent ( +\begin_inset Formula $\Gamma\vdash\beta$ +\end_inset +). + \end_layout -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $True$ +\begin_layout Standard +It is far from obvious how to overcome that difficulty. + Mathematicians have studied the constructive logic for more than 60 years, + trying to replace the rules in Table +\begin_inset space ~ \end_inset -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Proof-rules-of-constructive-and-boolean" +plural "false" +caps "false" +noprefix "false" \end_inset - - -\begin_inset Text -\begin_layout Plain Layout - -\size small -\begin_inset Formula $False$ + by a different but equivalent set of derivation rules that require no guessing + when looking for a proof. + The first partial success came in 1935 with an algorithm called +\begin_inset Quotes eld \end_inset - -\end_layout - +LJ +\begin_inset Quotes erd \end_inset - - -\begin_inset Text + +. +\begin_inset Foot +status open \begin_layout Plain Layout +See +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "https://en.wikipedia.org/wiki/Sequent_calculus#Overview" +literal "false" -\size small -\begin_inset Formula $True$ \end_inset \end_layout \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $False$ -\end_inset + The LJ algorithm still has a significant problem: one of its derivation + rules may be applied infinitely many times. + So, the LJ algorithm is not guaranteed to terminate without some heuristics + for avoiding an infinite loop. + A terminating version of the LJ algorithm, called +\begin_inset Index idx +status open +\begin_layout Plain Layout +LJT algorithm \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +LJT, was formulated in 1992. +\begin_inset Foot +status open -\size small -\begin_inset Formula $False$ +\begin_layout Plain Layout +An often cited paper by R. +\begin_inset space ~ \end_inset +Dyckhoff +\begin_inset Index idx +status open +\begin_layout Plain Layout +Roy Dyckhoff \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + is +\family typewriter -\size small -\begin_inset Formula $False$ -\end_inset +\begin_inset CommandInset href +LatexCommand href +target "https://philpapers.org/rec/DYCCSC" +literal "false" +\end_inset -\end_layout -\end_inset - - - - -\begin_inset Text +\family default +. + For the history of that research, see +\family typewriter -\begin_layout Plain Layout +\begin_inset CommandInset href +LatexCommand href +target "https://research-repository.st-andrews.ac.uk/handle/10023/8824" +literal "false" -\size small -\begin_inset Formula $False$ \end_inset \end_layout \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $True$ -\end_inset + +\end_layout +\begin_layout Standard +We will first present the LJ algorithm. + Although that algorithm does not guarantee termination, it is simpler to + understand and to apply by hand. + Then we will show how to modify the LJ algorithm in order to obtain the + always-terminating LJT algorithm. +\end_layout +\begin_layout Subsubsection* +The LJ algorithm \end_layout +\begin_layout Standard +Figure +\begin_inset space ~ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\size small -\begin_inset Formula $True$ +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Rules-of-the-LJ-algorithm" +plural "false" +caps "false" +noprefix "false" + \end_inset + shows the LJ algorithm's axioms and derivation rules. + Each rule says that the bottom sequent will be proved if proofs are given + for sequent(s) at the top. + For each possible sub-expression (conjunction +\begin_inset Formula $X\wedge Y$ +\end_inset -\end_layout +, disjunction +\begin_inset Formula $X\vee Y$ +\end_inset +, and implication +\begin_inset Formula $X\Rightarrow Y$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +) there is one rule where that sub-expression is a premise (at +\begin_inset Quotes eld +\end_inset -\size small -\begin_inset Formula $False$ +left +\begin_inset Quotes erd \end_inset +) and one rule where that sub-expression is the goal (at +\begin_inset Quotes eld +\end_inset -\end_layout +right +\begin_inset Quotes erd +\end_inset +). + Those sub-expressions are shown in blue in Figure +\begin_inset space ~ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\size small -\begin_inset Formula $True$ -\end_inset +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Rules-of-the-LJ-algorithm" +plural "false" +caps "false" +noprefix "false" +\end_inset + to help us look for a proof. + To find out which rules apply, we match some part of the sequent with a + blue sub-expression. \end_layout -\end_inset - - -\begin_inset Text +\begin_layout Standard +\begin_inset Float figure +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Box Boxed +position "t" +hor_pos "c" +has_inner_box 1 +inner_pos "t" +use_parbox 0 +use_makebox 0 +width "70line%" +special "none" +height "1in" +height_special "totalheight" +thickness "0.4pt" +separation "3pt" +shadowsize "4pt" +framecolor "black" +backgroundcolor "none" +status open \begin_layout Plain Layout +\begin_inset Formula +\begin{align*} +\frac{}{\Gamma,X\vdash{\color{blue}X}}~(\text{Id})\qquad & \qquad\frac{}{\Gamma\vdash{\color{blue}\top}}~(\text{True})\\ +\frac{\Gamma,A\Rightarrow B\vdash A\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\Rightarrow B}\vdash C}~(\text{Left}\Rightarrow)\qquad & \qquad\frac{\Gamma,A\vdash B}{\Gamma\vdash{\color{blue}A\Rightarrow B}}~(\text{Right}\Rightarrow)\\ +\frac{\Gamma,A_{i}\vdash C}{\Gamma,{\color{blue}A_{1}\wedge A_{2}}\vdash C}~(\text{Left}\wedge_{i})\qquad & \qquad\frac{\Gamma\vdash A\quad\quad\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\wedge B}}~(\text{Right}\wedge)\\ +\frac{\Gamma,A\vdash C\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\vee B}\vdash C}~(\text{Left}\vee)\qquad & \qquad\frac{\Gamma\vdash A_{i}}{\Gamma\vdash{\color{blue}A_{1}\vee A_{2}}}~(\text{Right}\vee_{i}) +\end{align*} -\size small -\begin_inset Formula $True$ \end_inset \end_layout \end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $False$ -\end_inset \end_layout -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout +\begin_inset Caption Standard -\size small -\begin_inset Formula $False$ +\begin_layout Plain Layout +Axioms and derivation rules of the LJ algorithm. + Each of the rules +\begin_inset Quotes eld \end_inset +( +\begin_inset Formula $\text{Left}\wedge_{i}$ +\end_inset -\end_layout - +) +\begin_inset Quotes erd \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + and +\begin_inset Quotes eld +\end_inset -\size small -\begin_inset Formula $False$ +( +\begin_inset Formula $\text{Right}\vee_{i}$ \end_inset +) +\begin_inset Quotes erd +\end_inset -\end_layout + have two versions, with +\begin_inset Formula $i=1$ +\end_inset + or +\begin_inset Formula $i=2$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +. + +\begin_inset CommandInset label +LatexCommand label +name "fig:Rules-of-the-LJ-algorithm" -\size small -\begin_inset Formula $False$ \end_inset \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\size small -\begin_inset Formula $True$ +\end_layout + \end_inset \end_layout +\begin_layout Standard +It turns out that the rules in Figure +\begin_inset space ~ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\size small -\begin_inset Formula $True$ +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Rules-of-the-LJ-algorithm" +plural "false" +caps "false" +noprefix "false" + \end_inset + are +\emph on +equivalent +\emph default + to the rules in Table +\begin_inset space ~ +\end_inset -\end_layout -\end_inset - - - +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Proof-rules-for-constructive-logic" +plural "false" +caps "false" +noprefix "false" \end_inset - +. + The proof is beyond the scope of this book. + We only remark that this equivalence is far from obvious. + To prove it, one needs to demonstrate that any sequent derived through + the first set of rules is also derivable through the second set, and vice + versa. \end_layout \begin_layout Standard -Using this table, we find that the formula -\begin_inset Formula $\alpha\Rightarrow\alpha$ +To illustrate the LJ algorithm, let us prove the sequent +\begin_inset space ~ \end_inset - has the value -\begin_inset Formula $True$ -\end_inset +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-sequent-2" +plural "false" +caps "false" +noprefix "false" - in all cases, whether -\begin_inset Formula $\alpha$ \end_inset - itself is -\begin_inset Formula $True$ +). + Denote that sequent by +\begin_inset Formula $S_{0}$ \end_inset - or -\begin_inset Formula $False$ -\end_inset +: +\begin_inset Formula +\[ +S_{0}\triangleq\emptyset\vdash\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\Rightarrow\beta\quad. +\] -. - This check is sufficient to show that -\begin_inset Formula $\forall\alpha.\,\alpha\Rightarrow\alpha$ \end_inset - is true in Boolean logic. -\end_layout + Since the goal of +\begin_inset Formula $S_{0}$ +\end_inset -\begin_layout Standard -Here is the truth table for the formulas -\begin_inset Formula $\forall(\alpha,\beta).\,(\alpha\wedge\beta)\Rightarrow\alpha$ + contains an implication, we use the rule +\begin_inset Quotes eld \end_inset - and -\begin_inset Formula $\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\wedge\beta)$ +( +\begin_inset Formula $\text{Right}\Rightarrow$ \end_inset -. - The first formula is true in Boolean logic since all values in its column - are -\begin_inset Formula $True$ +) +\begin_inset Quotes erd \end_inset -, while the second formula is not true since one value in the last column - is -\begin_inset Formula $False$ + and get a sequent +\begin_inset Formula $S_{1}$ \end_inset : -\end_layout +\begin_inset Formula +\[ +S_{1}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\beta\quad. +\] -\begin_layout Standard -\align center +\end_inset -\size small -\begin_inset Tabular - - - - - - - - - -\begin_inset Text +Now the implication is in the premise, so we use the rule +\begin_inset Quotes eld +\end_inset -\begin_layout Plain Layout +( +\begin_inset Formula $\text{Left}\Rightarrow$ +\end_inset -\size small -\begin_inset Formula $\alpha$ +) +\begin_inset Quotes erd \end_inset + and get two new sequents: +\begin_inset Formula +\[ +S_{2}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\alpha\Rightarrow\alpha\quad,\quad\quad S_{3}\triangleq\beta\vdash\beta\quad. +\] -\end_layout +\end_inset +Sequent +\begin_inset Formula $S_{3}$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + follows from the +\begin_inset Quotes eld +\end_inset -\size small -\begin_inset Formula $\beta$ +(Id) +\begin_inset Quotes erd \end_inset + axiom, so it remains to prove +\begin_inset Formula $S_{2}$ +\end_inset -\end_layout +. + Since +\begin_inset Formula $S_{2}$ +\end_inset + contains an implication both as a premise and as the goal, we may apply + either the rule +\begin_inset Quotes eld \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +( +\begin_inset Formula $\text{Left}\Rightarrow$ +\end_inset -\series bold -\size small -\begin_inset Formula $\alpha\wedge\beta$ +) +\begin_inset Quotes erd \end_inset + or the rule +\begin_inset Quotes eld +\end_inset -\end_layout +( +\begin_inset Formula $\text{Right}\Rightarrow$ +\end_inset +) +\begin_inset Quotes erd \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +. + We choose to apply +\begin_inset Quotes eld +\end_inset -\size small -\begin_inset Formula $(\alpha\wedge\beta)\Rightarrow\alpha$ +( +\begin_inset Formula $\text{Left}\Rightarrow$ \end_inset +) +\begin_inset Quotes erd +\end_inset -\end_layout + and get two new sequents: +\begin_inset Formula +\[ +S_{4}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\alpha\Rightarrow\alpha\quad,\quad\quad S_{5}:\beta\vdash\alpha\Rightarrow\alpha\quad. +\] \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +Notice that +\begin_inset Formula $S_{4}=S_{2}$ +\end_inset -\size small -\begin_inset Formula $\alpha\Rightarrow(\alpha\wedge\beta)$ +. + So, our proof search is getting into an infinite loop trying to prove the + same sequent +\begin_inset Formula $S_{2}$ \end_inset + over and over again. + We can prove +\begin_inset Formula $S_{5}$ +\end_inset + but this will not help us break the loop. \end_layout +\begin_layout Standard +Once we recognize the problem, we backtrack to the point where we chose + to apply +\begin_inset Quotes eld \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout - -\size small -\begin_inset Formula $True$ +( +\begin_inset Formula $\text{Left}\Rightarrow$ \end_inset - -\end_layout - +) +\begin_inset Quotes erd \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $True$ + to +\begin_inset Formula $S_{2}$ \end_inset +. + That was a bad choice, so let us instead apply +\begin_inset Quotes eld +\end_inset -\end_layout +( +\begin_inset Formula $\text{Right}\Rightarrow$ +\end_inset +) +\begin_inset Quotes erd \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + to +\begin_inset Formula $S_{2}$ +\end_inset -\size small -\begin_inset Formula $True$ +. + This yields a new sequent +\begin_inset Formula $S_{6}$ \end_inset +: +\begin_inset Formula +\[ +S_{6}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta,\alpha\vdash\alpha\quad. +\] -\end_layout +\end_inset +This sequent follows from the +\begin_inset Quotes eld \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +(Id) +\begin_inset Quotes erd +\end_inset -\size small -\begin_inset Formula $True$ +axiom. + There are no more sequents to prove, so the proof of +\begin_inset Formula $S_{0}$ \end_inset + is finished. + It can be drawn as a +\begin_inset Index idx +status open +\begin_layout Plain Layout +proof tree \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\size small -\begin_inset Formula $True$ +\series bold +proof tree +\series default + like this: +\begin_inset Formula +\[ +\xymatrix{\xyScaleY{1.5pc}\xyScaleX{4pc} & & & (\text{Id})\\ +\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[r]\sp(0.5){S_{2}}\ar[ru]\sp(0.6){S_{3}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){S_{6}} & (\text{Id}) +} +\] + \end_inset +The nodes of the proof tree are axioms or derivation rules, and the edges + are intermediate sequents required by the rules. + Some rule nodes branch into several sequents because some rules require + more than one new sequent to be proved. + The leaves of the tree are axioms that do not require proving any further + sequents. + +\end_layout +\begin_layout Subsubsection* +Extracting code from proofs \end_layout +\begin_layout Standard +According to the Curry-Howard correspondence, a sequent (such as +\begin_inset Formula $A,B,...,C\vdash X$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout - -\size small -\begin_inset Formula $True$ +) represents the task of writing a fully parametric code expression of type + +\begin_inset Formula $X$ \end_inset + that uses some given values of types +\begin_inset Formula $A$ +\end_inset -\end_layout - +, +\begin_inset Formula $B$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +, ..., +\begin_inset Formula $C$ +\end_inset -\size small -\begin_inset Formula $False$ +. + The sequent is true (i.e., can be proved) if that code expression can be + found. + So, the code serves as an +\begin_inset Quotes eld \end_inset +evidence of proof +\begin_inset Quotes erd +\end_inset + for the sequent. \end_layout -\end_inset - - -\begin_inset Text +\begin_layout Standard +\begin_inset Float figure +wide false +sideways false +status open \begin_layout Plain Layout +\align center \size small -\begin_inset Formula $False$ -\end_inset - - -\end_layout - -\end_inset - - -\begin_inset Text +\begin_inset Box Boxed +position "t" +hor_pos "c" +has_inner_box 1 +inner_pos "t" +use_parbox 0 +use_makebox 0 +width "100col%" +special "none" +height "1in" +height_special "totalheight" +thickness "0.4pt" +separation "3pt" +shadowsize "4pt" +framecolor "black" +backgroundcolor "none" +status open \begin_layout Plain Layout \size small -\begin_inset Formula $True$ +\begin_inset Formula +\begin{align*} +\frac{}{\Gamma,A\vdash{\color{blue}A}}~(\text{Id})\quad & \quad\text{Proof}\,(\Gamma,A\vdash A)_{\text{given }p^{:\Gamma},x^{:A}}=x\\ +\frac{}{\Gamma\vdash{\color{blue}\top}}~(\text{True})\quad & \quad\text{Proof}\,(\Gamma\vdash\top)_{\text{given }p^{:\Gamma}}=1\\ +\frac{\Gamma,A\Rightarrow B\vdash A\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\Rightarrow B}\vdash C}~(\text{Left}\Rightarrow)\quad & \quad\text{Proof}\,(\Gamma,A\Rightarrow B\vdash C)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}\\ + & \quad\quad=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p,b^{:B}}\\ +\text{where} & \quad b^{:B}\triangleq q\big(\text{Proof}\,(\Gamma,A\Rightarrow B\vdash A)_{\text{given }p,q}\big)\\ +\frac{\Gamma,A\vdash B}{\Gamma\vdash{\color{blue}A\Rightarrow B}}~(\text{Right}\Rightarrow)\quad & \quad\text{Proof}\,(\Gamma\vdash A\Rightarrow B)_{\text{given }p^{:\Gamma}}\\ + & \quad\quad=x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash B)_{\text{given }p^{:\Gamma},x^{:A}}\\ +\frac{\Gamma,A\vdash C}{\Gamma,{\color{blue}A\wedge B}\vdash C}~(\text{Left}\wedge_{1})\quad & \quad\text{Proof}\,(\Gamma,A\wedge B\vdash C)_{\text{given }p^{:\Gamma},(a^{:A}\times b^{:B})}\\ + & \quad\quad=\text{Proof}\,(\Gamma,A\vdash C)_{\text{given }p^{:\Gamma},a^{:A}}\\ +\frac{\Gamma,B\vdash C}{\Gamma,{\color{blue}A\wedge B}\vdash C}~(\text{Left}\wedge_{2})\quad & \quad\text{Proof}\,(\Gamma,A\wedge B\vdash C)_{\text{given }p^{:\Gamma},(a^{:A}\times b^{:B})}\\ + & \quad\quad=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p^{:\Gamma},b^{:B}}\\ +\frac{\Gamma\vdash A\quad\quad\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\wedge B}}~(\text{Right}\wedge)\quad & \quad\text{Proof}\,(\Gamma\vdash A\wedge B)_{\text{given }p^{:\Gamma}}\\ + & \quad\quad=\text{Proof}\,(\Gamma\vdash A)_{\text{given }p^{:\Gamma}}\\ + & \quad\quad\quad\times\text{Proof}\,(\Gamma\vdash B)_{\text{given }p^{:\Gamma}}\\ +\frac{\Gamma,A\vdash C\quad\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\vee B}\vdash C}~(\text{Left}\vee)\quad & \quad\text{Proof}\,(\Gamma,A\vee B\vdash C)_{\text{given }p^{:\Gamma},q^{:A+B}}\\ + & \quad\quad=q\triangleright\begin{array}{|c||c|} + & C\\ +\hline A & x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash C)_{\text{given }p,x}\\ +B & y^{:B}\rightarrow\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p,y} +\end{array}\\ +\frac{\Gamma\vdash A}{\Gamma\vdash{\color{blue}A\vee B}}~(\text{Right}\vee_{1})\quad & \quad\text{Proof}\,(\Gamma\vdash A\vee B)_{\text{given }p^{:\Gamma}}=\text{Proof}\,(\Gamma\vdash A)+\bbnum 0^{:B}\\ +\frac{\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\vee B}}~(\text{Right}\vee_{2})\quad & \quad\text{Proof}\,(\Gamma\vdash A\vee B)_{\text{given }p^{:\Gamma}}=\bbnum 0^{:A}+\text{Proof}\,(\Gamma\vdash B) +\end{align*} + \end_inset \end_layout \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $False$ -\end_inset \end_layout -\end_inset - - - - -\begin_inset Text +\begin_layout Plain Layout +\begin_inset Caption Standard \begin_layout Plain Layout +\begin_inset CommandInset label +LatexCommand label +name "fig:proof-transformers-for-LJ-rules" -\size small -\begin_inset Formula $False$ \end_inset - +Proof transformers for the rules of the LJ algorithm. \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\size small -\begin_inset Formula $True$ +\end_layout + \end_inset \end_layout +\begin_layout Standard +In the previous subsection, we have found a proof of the sequent +\begin_inset Formula $S_{0}$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout - -\size small -\begin_inset Formula $False$ +, which represents the task of writing a fully parametric function with + type signature +\begin_inset Formula $(\left(A\rightarrow A\right)\rightarrow B)\rightarrow B$ \end_inset +). + Let us now see how we can extract the code of that function from the proof + of the sequent +\begin_inset Formula $S_{0}$ +\end_inset +. \end_layout +\begin_layout Standard +We start from the leaves of the proof tree and move step by step towards + the initial sequent. + At each step, we shorten the proof tree by replacing some sequent by its + corresponding evidence-of-proof code. + Eventually we will replace the initial sequent by its corresponding code. + Let us see how this procedure works for the proof tree of the sequent +\begin_inset Formula $S_{0}$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + shown in the previous section. +\end_layout -\size small -\begin_inset Formula $True$ -\end_inset +\begin_layout Standard +Since the leaves are axioms, let us write the code corresponding to each + axiom of LJ: +\begin_inset Formula +\begin{align*} + & \frac{}{\Gamma,X\vdash X}~(\text{Id})\quad:\quad\quad\text{Proof}\,(\Gamma,X\vdash X)_{\text{given }p^{:\Gamma},x^{:X}}=x\quad;\\ + & \frac{}{\Gamma\vdash\top}~(\text{True})\quad:\quad\quad\text{Proof}\,(\Gamma\vdash\top)_{\text{given }p^{:\Gamma}}=1\quad. +\end{align*} +\end_inset -\end_layout +Here we denote explicitly the values (such as +\begin_inset Formula $p$ +\end_inset + and +\begin_inset Formula $x$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +) given as premises to the sequent. + The notation +\begin_inset Formula $p^{:\Gamma}$ +\end_inset -\size small -\begin_inset Formula $True$ + means all values given in the set of premises +\begin_inset Formula $\Gamma$ \end_inset +. + Below we will assume that the propositions +\begin_inset Formula $\alpha$ +\end_inset -\end_layout + and +\begin_inset Formula $\beta$ +\end_inset + correspond to types +\begin_inset Formula $A$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout + and +\begin_inset Formula $B$ +\end_inset -\size small -\begin_inset Formula $False$ +; that is, +\begin_inset Formula $\alpha\triangleq{\cal CH}(A)$ \end_inset + and +\begin_inset Formula $\beta\triangleq{\cal CH}(B)$ +\end_inset +. \end_layout +\begin_layout Standard +The leaves in the proof tree for +\begin_inset Formula $S_{0}$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + are the +\begin_inset Quotes eld +\end_inset -\size small -\begin_inset Formula $False$ +( +\begin_inset Formula $\text{Id}$ \end_inset +) +\begin_inset Quotes erd +\end_inset -\end_layout + axioms used to prove the sequents +\begin_inset Formula $S_{3}$ +\end_inset + and +\begin_inset Formula $S_{6}$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +. + Let us write the code that serves as the +\begin_inset Quotes eld +\end_inset -\size small -\begin_inset Formula $False$ +evidence of proof +\begin_inset Quotes erd \end_inset + for these sequents. + For brevity, we denote +\begin_inset Formula $\gamma\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta$ +\end_inset -\end_layout + and +\begin_inset Formula $C\triangleq\left(A\rightarrow A\right)\rightarrow B$ +\end_inset +, so that +\begin_inset Formula $\gamma={\cal CH}(C)$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +. + Then we can write: +\begin_inset Formula +\begin{align*} + & S_{3}\triangleq\beta\vdash\beta\quad,\quad\quad\text{Proof}\,(S_{3})_{\text{given }y^{:B}}=y\quad,\\ + & S_{6}\triangleq\gamma,\alpha\vdash\alpha\quad,\quad\quad\text{Proof}\,(S_{6})_{\text{given }q^{:C},x^{:A}}=x\quad. +\end{align*} -\size small -\begin_inset Formula $True$ \end_inset +Note that the proof of +\begin_inset Formula $S_{6}$ +\end_inset -\end_layout + does not use the first given value +\begin_inset Formula $q^{:C}$ +\end_inset + (corresponding to the premise +\begin_inset Formula $\gamma$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +). +\end_layout -\size small -\begin_inset Formula $True$ +\begin_layout Standard +We now shorten the proof tree by replacing the sequents +\begin_inset Formula $S_{3}$ \end_inset + and +\begin_inset Formula $S_{6}$ +\end_inset -\end_layout + by their +\begin_inset Quotes eld +\end_inset +evidence of proof +\begin_inset Quotes erd \end_inset - - - + +: +\begin_inset Formula +\[ +\xymatrix{\xyScaleY{1.5pc}\xyScaleX{4pc} & & & \square\\ +\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[r]\sp(0.5){S_{2}}\ar[ru]\sp(0.6){(y)_{\text{given }y^{:B}}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){(x)_{\text{given }q^{:C},x^{:A}}} & \square +} +\] \end_inset @@ -10034,657 +10408,768 @@ Here is the truth table for the formulas \end_layout \begin_layout Standard -Table -\begin_inset space ~ +The next step is to consider the proof of +\begin_inset Formula $S_{2}$ \end_inset +, which is found by applying the rule +\begin_inset Quotes eld +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Logical-formulas-Boolean-theorems" -plural "false" -caps "false" -noprefix "false" +( +\begin_inset Formula $\text{Right}\Rightarrow$ +\end_inset +) +\begin_inset Quotes erd \end_inset - shows more examples of logical formulas that are true in Boolean logic. - Each formula is first written in terms of -\begin_inset Formula ${\cal CH}$ +. + This rule promises to give a proof of +\begin_inset Formula $S_{2}$ \end_inset --propositions (we denote -\begin_inset Formula $\alpha\triangleq{\cal CH}(A)$ + if we have a proof of +\begin_inset Formula $S_{6}$ \end_inset - and -\begin_inset Formula $\beta\triangleq{\cal CH}(B)$ +. + In order to extract code from that rule, we can write a function that transform +s a proof of +\begin_inset Formula $S_{6}$ \end_inset - for brevity) and then as a Scala type signature of a function. - So, all these type signatures -\emph on -can -\emph default - be implemented. -\end_layout + into a proof of +\begin_inset Formula $S_{2}$ +\end_inset -\begin_layout Standard -\begin_inset Float table -placement h -wide false -sideways false -status open +. + We call this function the +\series bold +proof transformer +\series default -\begin_layout Plain Layout -\align center -\begin_inset Tabular - - - - - - - -\begin_inset Text +\begin_inset Index idx +status open \begin_layout Plain Layout - -\series bold -\size small -Logic formula +Curry-Howard correspondence!proof transformer \end_layout \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\series bold -\size small -Type formula -\end_layout -\end_inset - - -\begin_inset Text +\begin_inset Index idx +status open \begin_layout Plain Layout - -\series bold -\size small -Scala code +proof transformer \end_layout \end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -\size footnotesize -\begin_inset Formula $\forall\alpha.\,\alpha\Rightarrow\alpha$ + corresponding to the rule +\begin_inset Quotes eld \end_inset +( +\begin_inset Formula $\text{Right}\Rightarrow$ +\end_inset -\end_layout - +) +\begin_inset Quotes erd \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +. + That rule and its transformer are defined as: +\begin_inset Formula +\begin{align*} +\frac{\Gamma,A\vdash B}{\Gamma\vdash A\Rightarrow B}~(\text{Right}\Rightarrow)\quad: & \quad\quad\text{Proof}\,(\Gamma\vdash A\Rightarrow B)_{\text{given }p^{:\Gamma}}\\ + & \quad\quad=x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash B)_{\text{given }p^{:\Gamma},x^{:A}}\quad. +\end{align*} -\size footnotesize -\begin_inset Formula $\forall A.\,A\rightarrow A$ \end_inset +Applying the proof transformer to the known proof of +\begin_inset Formula $S_{6}$ +\end_inset -\end_layout - +, we obtain a proof of +\begin_inset Formula $S_{2}$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\begin_inset listings -inline true -status open +: +\begin_inset Formula +\[ +\text{Proof}\,(S_{2})_{\text{given }q^{:C}}=x^{:A}\rightarrow\text{Proof}\,(S_{6})_{\text{given }q^{:C},x^{:A}}=(x^{:A}\rightarrow x)_{\text{given }q^{:C}}\quad. +\] -\begin_layout Plain Layout +\end_inset -def id[A](x: A): A = x -\end_layout +The proof tree can be now shortened to: +\begin_inset Formula +\[ +\xymatrix{\xyScaleY{1.5pc}\xyScaleX{3.5pc} & & & \square\\ +\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[rr]\sp(0.62){(x^{:A}\rightarrow x)_{\text{given }q^{:C}}}\ar[ru]\sp(0.6){(y)_{\text{given }y^{:B}}} & & \square +} +\] \end_inset \end_layout +\begin_layout Standard +The next step is to get the proof of +\begin_inset Formula $S_{1}$ \end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -\size footnotesize -\begin_inset Formula $\forall\alpha.\,\alpha\Rightarrow True$ + obtained by applying the rule +\begin_inset Quotes eld \end_inset +( +\begin_inset Formula $\text{Left}\Rightarrow$ +\end_inset -\end_layout - +) +\begin_inset Quotes erd \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +. + That rule requires two previous sequents, so its transformer is a function + of two previously obtained proofs: +\begin_inset Formula +\begin{align*} + & \frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,A\Rightarrow B\vdash C}~(\text{Left}\Rightarrow)\quad:\\ + & \text{Proof}\,(\Gamma,A\Rightarrow B\vdash C)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p^{:\Gamma},b^{:B}}\\ + & \quad\quad\text{where}\quad b^{:B}\triangleq q\big(\text{Proof}\,(\Gamma,A\Rightarrow B\vdash A)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}\big)\quad. +\end{align*} -\size footnotesize -\begin_inset Formula $\forall A.\,A\rightarrow\bbnum 1$ \end_inset +In the proof tree shown above, we obtain a proof of +\begin_inset Formula $S_{1}$ +\end_inset -\end_layout + by applying this proof transformer to the proofs of +\begin_inset Formula $S_{2}$ +\end_inset + and +\begin_inset Formula $S_{3}$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\begin_inset listings -inline true -status open +: +\begin_inset Formula +\begin{align*} + & \text{Proof}\,(S_{1})_{\text{given }q^{:C}}=\text{Proof}\,(S_{3})_{\text{given }b^{:B}}\text{ where }b^{:B}\triangleq q(\text{Proof}\,(S_{2}))_{\text{given }q^{:C}}\\ + & \quad=b\text{ where }b^{:B}\triangleq q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}=q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}\quad. +\end{align*} -\begin_layout Plain Layout +\end_inset -def toUnit[A](x: A): Unit = () -\end_layout +Substituting this proof into the proof tree, we shorten the tree to: +\begin_inset Formula +\[ +\xymatrix{\xyScaleY{1.5pc}\xyScaleX{3.5pc}\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}} & \square} +\] \end_inset \end_layout +\begin_layout Standard +It remains to obtain the proof of +\begin_inset Formula $S_{0}$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout + by applying the proof transformer of the rule +\begin_inset Quotes eld +\end_inset -\size footnotesize -\begin_inset Formula $\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\vee\beta)$ +( +\begin_inset Formula $\text{Right}\Rightarrow$ \end_inset +) +\begin_inset Quotes erd +\end_inset -\end_layout +: +\begin_inset Formula +\begin{align*} + & \text{Proof}\,(S_{0})=\text{Proof}\,(\emptyset\vdash(\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta)\\ + & =q^{:(A\rightarrow A)\rightarrow B}\rightarrow\text{Proof}\,(S_{1})_{\text{given }q^{:C}}=q^{:(A\rightarrow A)\rightarrow B}\rightarrow q(x^{:A}\rightarrow x)\quad. +\end{align*} \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +The proof tree is now shortened to the code expression +\begin_inset Formula $q^{:(A\rightarrow A)\rightarrow B}\rightarrow q(x^{:A}\rightarrow x)$ +\end_inset -\size footnotesize -\begin_inset Formula $\forall(A,B).\,A\rightarrow A+B$ + has type +\begin_inset Formula $\left(\left(A\rightarrow A\right)\rightarrow B\right)\rightarrow B$ \end_inset +. + So, that code is an evidence of proof for +\begin_inset Formula $S_{0}$ +\end_inset +. + In this way, we have derived the code of a fully parametric function from + its type signature. \end_layout +\begin_layout Standard +Figure +\begin_inset space ~ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\begin_inset listings -inline true -status open -\begin_layout Plain Layout - -def toL[A, B](x: A): Either[A, B] = Left(x) -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:proof-transformers-for-LJ-rules" +plural "false" +caps "false" +noprefix "false" \end_inset + shows the proof transformers for all the rules of the LJ algorithm. + Apart from the special rule +\begin_inset Quotes eld +\end_inset -\end_layout - +( +\begin_inset Formula $\text{Left}\Rightarrow$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout +) +\begin_inset Quotes erd +\end_inset -\size footnotesize -\begin_inset Formula $\forall(\alpha,\beta).\,(\alpha\wedge\beta)\Rightarrow\alpha$ +, all other rules have proof transformers using just one of the code constructio +ns ( +\begin_inset Quotes eld \end_inset +create function +\begin_inset Quotes erd +\end_inset -\end_layout +, +\begin_inset Quotes eld +\end_inset +create tuple +\begin_inset Quotes erd \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +, +\begin_inset Quotes eld +\end_inset -\size footnotesize -\begin_inset Formula $\forall(A,B).\,A\times B\rightarrow A$ +use tuple +\begin_inset Quotes erd \end_inset +, etc.) allowed within fully parametric code. +\end_layout +\begin_layout Subsubsection* +The LJT algorithm \end_layout +\begin_layout Standard +As we have seen, the LJ algorithm enters a loop if the rule +\begin_inset Quotes eld \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -def first[A, B](p: (A, B)): A = p._1 -\end_layout +( +\begin_inset Formula $\text{Left}\Rightarrow$ \end_inset - -\end_layout - +) +\begin_inset Quotes erd \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout + gives a sequent we already had at a previous step. + That rule requires us to prove two new sequents: +\begin_inset Formula +\[ +\frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,A\Rightarrow B\vdash C}~(\text{Left}\Rightarrow)\quad. +\] -\size footnotesize -\begin_inset Formula $\forall(\alpha,\beta).\,\alpha\Rightarrow(\beta\Rightarrow\alpha)$ \end_inset - -\end_layout - +A sign of trouble is that the first of these sequents ( +\begin_inset Formula $\Gamma,A\Rightarrow B\vdash A$ \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\size footnotesize -\begin_inset Formula $\forall(A,B).\,A\rightarrow(B\rightarrow A)$ +) does not have a simpler form than the initial sequent ( +\begin_inset Formula $\Gamma,A\Rightarrow B\vdash C$ \end_inset +). + So, it is not clear that we are getting closer to completing the proof. + If +\begin_inset Formula $A=C$ +\end_inset +, the new sequent will simply repeat the initial sequent, immediately creating + a loop. \end_layout +\begin_layout Standard +In some cases, a repeated sequent will occur after more than one step. + It is not easy to formulate rigorous conditions for stopping the loop or + for avoiding the rule +\begin_inset Quotes eld \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -def const[A, B](x: A): B => A = (_ => x) -\end_layout +( +\begin_inset Formula $\text{Left}\Rightarrow$ \end_inset +) +\begin_inset Quotes erd +\end_inset +. \end_layout +\begin_layout Standard +The LJT algorithm solves this problem by removing the rule +\begin_inset Quotes eld \end_inset - - - +( +\begin_inset Formula $\text{Left}\Rightarrow$ \end_inset +) +\begin_inset Quotes erd +\end_inset -\end_layout + from the LJ algorithm. + Instead, +\emph on +four +\emph default + new rules are introduced. + Each of these rules contains a different pattern instead of +\begin_inset Formula $A$ +\end_inset -\begin_layout Plain Layout -\begin_inset Caption Standard + in the premise +\begin_inset Formula $A\Rightarrow C$ +\end_inset -\begin_layout Plain Layout -Examples of logical formulas that are true theorems in Boolean logic. -\begin_inset CommandInset label -LatexCommand label -name "tab:Logical-formulas-Boolean-theorems" +: +\begin_inset Formula +\begin{align*} +\text{(}A\text{ is atomic)\,}\frac{\Gamma,A,B\vdash D}{\Gamma,A,{\color{blue}A\Rightarrow B}\vdash D}~(\text{Left}\Rightarrow_{A})\qquad & \quad\frac{\Gamma,A\Rightarrow B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\wedge B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\wedge})\\ +\frac{\Gamma,B\Rightarrow C\vdash A\Rightarrow B\quad\quad\Gamma,C\vdash D}{\Gamma,{\color{blue}(A\Rightarrow B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\Rightarrow})\qquad & \quad\frac{\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\vee B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\vee}) +\end{align*} \end_inset +The rule +\begin_inset Quotes eld +\end_inset -\end_layout +\begin_inset Formula $\text{Left}\Rightarrow_{A}$ \end_inset -\end_layout +\begin_inset Quotes erd +\end_inset + applies only if the implication starts with an +\begin_inset Quotes eld \end_inset +atomic +\begin_inset Quotes erd +\end_inset + type expression, i.e., a single type parameter or a unit type. + In all other cases, the implication must start with a conjunction, a disjunctio +n, or an implication, which means that one of the three remaining rules + will apply. \end_layout \begin_layout Standard -Table +The LJT algorithm retains all the rules in Figure \begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "tab:Logical-formulas-not-Boolean-theorems" +reference "fig:proof-transformers-for-LJ-rules" plural "false" caps "false" noprefix "false" \end_inset - some examples of formulas that are -\emph on -not true -\emph default - in Boolean logic. - Translated into type formulas and then into Scala, these formulas yield - type signatures that -\emph on -cannot -\emph default - be implemented by fully parametric functions. -\end_layout - -\begin_layout Standard -\begin_inset Float table -placement h -wide false -sideways false -status open - -\begin_layout Plain Layout -\align center -\begin_inset Tabular - - - - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\series bold -\size small -Logic formula -\end_layout + except the rule +\begin_inset Quotes eld +\end_inset +( +\begin_inset Formula $\text{Left}\Rightarrow$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +) +\begin_inset Quotes erd +\end_inset -\series bold -\size small -Type formula +, which is replaced by the four new rules. + It is far from obvious that the new rules are equivalent to the old ones. + It took mathematicians several decades to come up with the LJT rules and + to prove their validity. + This book will rely on that result and will not attempt to prove it. \end_layout +\begin_layout Standard +The proof transformers for the new rules are shown in Figure +\begin_inset space ~ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\series bold -\size small -Scala type signature -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:proof-transformers-for-LJT-rules" +plural "false" +caps "false" +noprefix "false" \end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -\size footnotesize -\begin_inset Formula $\forall\alpha.\,True\Rightarrow\alpha$ +. + Figures +\begin_inset space ~ \end_inset -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:proof-transformers-for-LJ-rules" +plural "false" +caps "false" +noprefix "false" \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +– +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:proof-transformers-for-LJT-rules" +plural "false" +caps "false" +noprefix "false" -\size footnotesize -\begin_inset Formula $\forall A.\,\bbnum 1\rightarrow A$ \end_inset + define the set of proof transformers sufficient for using the LJT algorithm + in practice. + The +\begin_inset Index idx +status open + +\begin_layout Plain Layout +\family typewriter +curryhoward +\family default + library \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + \begin_inset listings inline true status open \begin_layout Plain Layout -def f[A](x: Unit): A +curryhoward \end_layout \end_inset + library +\family typewriter -\end_layout - -\end_inset - - - - -\begin_inset Text +\begin_inset Foot +status open \begin_layout Plain Layout +See +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "https://github.com/Chymyst/curryhoward" -\size footnotesize -\begin_inset Formula $\forall(\alpha,\beta).\,(\alpha\vee\beta)\Rightarrow\alpha$ \end_inset \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\size footnotesize -\begin_inset Formula $\forall(A,B).\,A+B\rightarrow A$ +\family default + implements these proof transformers. +\end_layout + +\begin_layout Standard +The most complicated of the new rules is the rule +\begin_inset Quotes eld \end_inset +( +\begin_inset Formula $\text{Left}\Rightarrow_{\Rightarrow}$ +\end_inset -\end_layout +) +\begin_inset Quotes erd +\end_inset +. + It is far from obvious why the rule +\begin_inset Formula $\text{Left}\Rightarrow_{\Rightarrow}$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\begin_inset listings -inline true -status open + is useful or even correct. + This rule is based on a non-trivial logic identity: +\begin_inset Formula +\[ +\left(\left(A\rightarrow B\right)\rightarrow C\right)\rightarrow\left(A\rightarrow B\right)\,\Longleftrightarrow\,\left(B\rightarrow C\right)\rightarrow\left(A\rightarrow B\right)\quad. +\] -\begin_layout Plain Layout +\end_inset -def f[A,B](x: Either[A, B]): A -\end_layout +Consider the type at the left-hand side of this identity: +\begin_inset Formula +\[ +\left(\left(A\rightarrow B\right)\rightarrow C\right)\rightarrow B\rightarrow C\quad. +\] \end_inset +A function with that type can be written as: +\begin_inset Formula +\[ +f=k^{:\left(A\rightarrow B\right)\rightarrow C}\rightarrow b^{:B}\rightarrow k\,(\_^{:A}\rightarrow b)\quad. +\] -\end_layout +\end_inset +The function +\begin_inset Formula $f$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout + occurs in the proof transformer for the rule +\begin_inset Formula $\text{Left}\Rightarrow_{\Rightarrow}$ +\end_inset -\size footnotesize -\begin_inset Formula $\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\wedge\beta)$ + (shown below in Table +\begin_inset space ~ \end_inset -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:proof-transformers-for-LJT-rules" +plural "false" +caps "false" +noprefix "false" \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +). + Note that this +\begin_inset Formula $f$ +\end_inset -\size footnotesize -\begin_inset Formula $\forall(A,B).\,A\rightarrow A\times B$ + applies +\begin_inset Formula $k$ +\end_inset + + to a function +\begin_inset Formula $(\_\rightarrow b)$ \end_inset + that ignores its argument. + We expect to be able to simplify the resulting expression at the place + when +\begin_inset Formula $(\_\rightarrow b)$ +\end_inset + + is applied to some argument expression, which we can then ignore. + For this reason, applying the transformer for the rule +\begin_inset Formula $\text{Left}\Rightarrow_{\Rightarrow}$ +\end_inset + results in evidence-of-proof code that is longer than the code obtained + via LJ's rule transformers. + The code obtained via the LJT algorithm needs to be simplified symbolically. \end_layout +\begin_layout Standard +As an example of using the LJT algorithm, we again prove the sequent from + the previous section: +\begin_inset Formula $S_{0}=\emptyset\vdash((\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\begin_inset listings -inline true -status open +. + At each step, only one LJT rule applies to each sequent. + The initial part of the proof tree looks like this: +\begin_inset VSpace -100baselineskip% +\end_inset -\begin_layout Plain Layout -def f[A,B](p: A): (A, B) -\end_layout +\begin_inset Formula +\[ +\xymatrix{\xyScaleY{1.5pc}\xyScaleX{6pc} & & & ~\\ +\ar[r]\sp(0.4){\emptyset\vdash((\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){(\alpha\Rightarrow\alpha)\Rightarrow\beta\vdash\beta} & (\text{Left}\Rightarrow_{\Rightarrow})\ar[ru]\sp(0.65){\beta\vdash\beta}\ar[r]\sp(0.65){\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha} & ~ +} +\] \end_inset +The proofs for the sequents +\begin_inset Formula $\beta\vdash\beta$ +\end_inset -\end_layout - + and +\begin_inset Formula $\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout + are the same as before: +\begin_inset Formula +\[ +\text{Proof}\,(\beta\vdash\beta)_{\text{given }y^{:B}}=y\quad,\quad\quad\text{Proof}\,(\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha)_{\text{given }r^{:A\rightarrow B}}=x^{:A}\rightarrow x\quad. +\] -\size footnotesize -\begin_inset Formula $\forall(\alpha,\beta).\,(\alpha\Rightarrow\beta)\Rightarrow\alpha$ \end_inset +Substituting these proofs into the proof transformer of the rule +\begin_inset Quotes eld +\end_inset -\end_layout +( +\begin_inset Formula $\text{Left}\Rightarrow_{\Rightarrow}$ +\end_inset +) +\begin_inset Quotes erd \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + produces this code: +\begin_inset Formula +\begin{align*} + & \text{Proof}\,((\alpha\Rightarrow\alpha)\Rightarrow\beta\vdash\beta)_{\text{given }q^{:(A\rightarrow A)\rightarrow B}}=q\big(\text{Proof}\,(\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha)_{\text{given }r^{:A\rightarrow B}}\big)\\ + & \quad\quad\text{where }r^{:A\rightarrow B}=a^{:A}\rightarrow q(\_^{:A}\rightarrow a)\\ + & =q(x^{:A}\rightarrow x)\quad. +\end{align*} -\size footnotesize -\begin_inset Formula $\forall(A,B).\,(A\rightarrow B)\rightarrow A$ \end_inset +The proof of +\begin_inset Formula $\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha$ +\end_inset -\end_layout + does not actually use the intermediate value +\begin_inset Formula $r^{:A\rightarrow B}$ +\end_inset + provided by the proof transformer. + As a symbolic simplification step, we may simply omit the code of +\begin_inset Formula $r$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +. + The \begin_inset listings inline true status open \begin_layout Plain Layout -def f[A,B](x: A => B): A +curryhoward \end_layout \end_inset - + library always performs symbolic simplification after applying the LJT + algorithm. + \end_layout +\begin_layout Standard +\begin_inset Float figure +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center + +\size small +\begin_inset Box Boxed +position "t" +hor_pos "c" +has_inner_box 1 +inner_pos "t" +use_parbox 0 +use_makebox 0 +width "100col%" +special "none" +height "1in" +height_special "totalheight" +thickness "0.4pt" +separation "3pt" +shadowsize "4pt" +framecolor "black" +backgroundcolor "none" +status open + +\begin_layout Plain Layout + +\size small +\begin_inset Formula +\begin{align*} +\frac{\Gamma,A,B\vdash D}{\Gamma,A,{\color{blue}A\Rightarrow B}\vdash D}~(\text{Left}\Rightarrow_{A})\quad & \quad\text{Proof}\,(\Gamma,A,A\Rightarrow B\vdash D)_{\text{given }p^{:\Gamma},x^{:A},q^{:A\rightarrow B}}\\ + & \quad\quad=\text{Proof}\,(\Gamma,A,B\vdash D)_{\text{given }p,x,q(x)}\\ +\frac{\Gamma,A\Rightarrow B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\wedge B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\wedge})\quad & \quad\text{Proof}\,(\Gamma,(A\wedge B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:A\times B\rightarrow C}}\\ + & \quad\quad=\text{Proof}\,(\Gamma,\\ + & \quad\quad\quad A\Rightarrow B\Rightarrow C\vdash D)_{\text{given }p,(a^{:A}\rightarrow b^{:B}\rightarrow q(a\times b))}\\ +\frac{\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\vee B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\vee})\quad & \quad\text{Proof}\,(\Gamma,(A\vee B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:A+B\rightarrow C}}\\ + & \quad\quad=\text{Proof}\,(\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D)_{\text{given }p,r,s}\\ + & \quad\quad\text{where}~r\triangleq a^{:A}\rightarrow q(a+\bbnum 0)\\ + & \quad\quad\text{ and }s\triangleq b^{:B}\rightarrow q(\bbnum 0+b)\\ +\frac{\Gamma,B\Rightarrow C\vdash A\Rightarrow B\quad\Gamma,C\vdash D}{\Gamma,{\color{blue}(A\Rightarrow B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\Rightarrow})\quad & \quad\text{Proof}\,(\Gamma,(A\Rightarrow B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:\left(A\rightarrow B\right)\rightarrow C}}\\ + & \quad\quad=\text{Proof}\,(\Gamma,C\vdash D)_{\text{given }p,c}\\ + & \quad\quad\text{ where}~c^{:C}\triangleq q\big(\text{Proof}\,(\Gamma,\\ + & \quad\quad\quad\quad\quad\quad\quad\quad B\Rightarrow C\vdash A\Rightarrow B)_{\text{given }p,r}\big)\\ + & \quad\quad\text{ and }r^{:B\rightarrow C}\triangleq b^{:B}\rightarrow q(\_^{:A}\rightarrow b) +\end{align*} + \end_inset - - - + + +\end_layout \end_inset @@ -10695,18 +11180,23 @@ def f[A,B](x: A => B): A \begin_inset Caption Standard \begin_layout Plain Layout -Examples of logical formulas that are -\emph on -not -\emph default - true in Boolean logic. \begin_inset CommandInset label LatexCommand label -name "tab:Logical-formulas-not-Boolean-theorems" +name "fig:proof-transformers-for-LJT-rules" \end_inset +Proof transformers for the four new rules of the +\begin_inset Index idx +status open + +\begin_layout Plain Layout +LJT algorithm|textit +\end_layout + +\end_inset +LJT algorithm. \end_layout \end_inset @@ -10720,2373 +11210,1964 @@ name "tab:Logical-formulas-not-Boolean-theorems" \end_layout \begin_layout Standard -At first sight, it may appear from these examples that whenever a logical - formula is true in Boolean logic, the corresponding type signature can - be implemented in code, and vice versa. - However, this is -\emph on -incorrect -\emph default -: the rules of Boolean logic are not fully suitable for reasoning about - types in a functional language. - We will now show some examples of formulas that are true in Boolean logic - but correspond to unimplementable type signatures. -\end_layout - -\begin_layout Standard -The first example is given by the following type: -\begin_inset Formula -\begin{equation} -\forall(A,B,C).\,\left(A\rightarrow B+C\right)\rightarrow\left(A\rightarrow B\right)+\left(A\rightarrow C\right)\quad,\label{eq:ch-example-boolean-bad-type} -\end{equation} - -\end_inset - -which corresponds to the Scala type signature: -\begin_inset listings -inline false +The reason the LJT algorithm terminates is that each rule replaces a given + sequent by one or more sequents with simpler premises or goals. +\begin_inset Foot status open \begin_layout Plain Layout +The paper +\family typewriter -def bad[A, B, C](g: A => Either[B, C]): Either[A => B, A => C] = ??? -\end_layout +\begin_inset CommandInset href +LatexCommand href +target "http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.35.2618" +literal "false" \end_inset -The function -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -bad +\family default + shows that the LJT algorithm terminates by giving an explicit decreasing + measure on proof trees. \end_layout \end_inset - -\emph on -cannot -\emph default - be implemented via fully parametric code. - To see why, consider that the only available data is a function -\begin_inset Formula $g^{:A\rightarrow B+C}$ + This guarantees that the proof search will terminate either with a complete + proof or with a sequent to which no more rules apply. + An example of such a +\begin_inset Quotes eld \end_inset -, which returns values of type -\begin_inset Formula $B$ +dead-end +\begin_inset Quotes erd \end_inset - or -\begin_inset Formula $C$ + sequent is +\begin_inset Formula $\alpha\vdash\beta$ \end_inset - depending (in some unknown way) on the input value of type -\begin_inset Formula $A$ + where +\begin_inset Formula $\alpha$ \end_inset -. - The function -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout + and +\begin_inset Formula $\beta$ +\end_inset -bad + are different, unrelated propositions. + When no more rules apply, the LJT algorithm concludes that the initial + sequent cannot be proved. \end_layout -\end_inset +\begin_layout Standard +\begin_inset Note Note +status collapsed - must return either a function of type -\begin_inset Formula $A\rightarrow B$ +\begin_layout Plain Layout +\begin_inset Separator parbreak \end_inset - or a function of type -\begin_inset Formula $A\rightarrow C$ -\end_inset -. - How can the code of -\begin_inset listings -inline true -status open +\end_layout \begin_layout Plain Layout +Proof search V: From deduction rules to code +\end_layout -bad +\begin_layout Plain Layout +The new rules are equivalent to the old rules, therefore... \end_layout +\begin_layout Plain Layout +Proof of a sequent +\begin_inset Formula $A,B,C\vdash G$ \end_inset - make that decision? The only input data is the function -\begin_inset Formula $g$ + +\begin_inset Formula $\Leftrightarrow$ \end_inset - that takes an argument of type -\begin_inset Formula $A$ + code/expression +\begin_inset Formula $t(a,b,c):G$ \end_inset -. - We could imagine applying -\begin_inset Formula $g$ -\end_inset - to various arguments of type -\begin_inset Formula $A$ -\end_inset +\end_layout - and to see whether -\begin_inset Formula $g$ +\begin_layout Plain Layout +Also can be seen as a function +\begin_inset Formula $t$ \end_inset - returns a -\begin_inset Formula $B$ + from +\begin_inset Formula $A,B,C$ \end_inset - or a -\begin_inset Formula $C$ + to +\begin_inset Formula $G$ \end_inset -. - However, the type -\begin_inset Formula $A$ -\end_inset - is arbitrary, and a fully parametric function cannot produce a value of - type -\begin_inset Formula $A$ -\end_inset +\end_layout - in order to apply -\begin_inset Formula $g$ -\end_inset +\begin_layout Plain Layout +Sequent in a proof follows from an axiom or from a transforming rule +\end_layout - to it. - So, the decision about whether to return -\begin_inset Formula $A\rightarrow B$ +\begin_layout Plain Layout +The two axioms are fixed expressions, +\begin_inset Formula $x^{A}\rightarrow x$ \end_inset - or -\begin_inset Formula $A\rightarrow C$ + and +\begin_inset Formula $1$ \end_inset - must be independent of -\begin_inset Formula $g$ -\end_inset -. - That decision must be hard-coded in the function -\begin_inset listings -inline true -status open +\end_layout \begin_layout Plain Layout +Each rule has a +\emph on +proof transformer +\emph default + function: +\begin_inset Formula $\text{PT}_{R\rightarrow}$ +\end_inset -bad -\end_layout - + , +\begin_inset Formula $\text{PT}_{L+}$ \end_inset -. + , etc. \end_layout -\begin_layout Standard -Suppose we hard-coded the decision to return a function of type -\begin_inset Formula $A\rightarrow B$ +\begin_layout Plain Layout +Examples of proof transformer functions: +\begin_inset Formula +\begin{align*} +\frac{\Gamma,A\vdash C\quad\;\Gamma,B\vdash C}{\Gamma,{\color{blue}A+B}\vdash C}\,L+\\ +PT_{L+}(t_{1}^{A\rightarrow C},t_{2}^{B\rightarrow C})=x^{A+B}\rightarrow & \ x\ \text{match}\begin{cases} +a^{A}\rightarrow t_{1}(a)\\ +b^{B}\rightarrow t_{2}(b) +\end{cases} +\end{align*} + \end_inset -. - How would we create a function of type -\begin_inset Formula $A\rightarrow B$ + +\begin_inset Formula +\begin{align*} +\frac{\Gamma,A\rightarrow B\rightarrow C\vdash D}{\Gamma,{\color{blue}(A\times B)\rightarrow C}\vdash D}\,L\rightarrow_{2}\\ +PT_{L\rightarrow_{2}}(f^{\left(A\rightarrow B\rightarrow C\right)\rightarrow D})=g^{A\times B\rightarrow C}\rightarrow & f\,(x^{A}\rightarrow y^{B}\rightarrow g(x,y)) +\end{align*} + \end_inset - in the body of -\begin_inset listings -inline true -status open -\begin_layout Plain Layout +\end_layout -bad +\begin_layout Plain Layout +Verify that we can indeed produce PTs for every rule of LJT \end_layout +\begin_layout Plain Layout +\begin_inset Separator parbreak \end_inset -? Given a value -\begin_inset Formula $x^{:A}$ -\end_inset - of type -\begin_inset Formula $A$ -\end_inset +\end_layout -, we would need to compute some value of type -\begin_inset Formula $B$ -\end_inset +\begin_layout Plain Layout +Proof search example II: deriving code +\end_layout -. - Since the type -\begin_inset Formula $B$ +\begin_layout Plain Layout +Once a proof tree is found, start from leaves and apply PTs +\end_layout + +\begin_layout Plain Layout +For each sequent +\begin_inset Formula $S_{i}$ \end_inset - is arbitrary (it is a type parameter), we cannot produce a value of type +, this will derive a +\series bold +proof expression +\series default -\begin_inset Formula $B$ +\begin_inset Formula $t_{i}$ \end_inset - from scratch. - The only potential source of values of type -\begin_inset Formula $B$ + +\end_layout + +\begin_layout Plain Layout +Example: to prove +\begin_inset Formula $S_{0}$ \end_inset - is the given function -\begin_inset Formula $g$ +, start from +\begin_inset Formula $S_{6}$ \end_inset -. - The only way of using -\begin_inset Formula $g$ -\end_inset + backwards: +\size footnotesize - is to apply it to -\begin_inset Formula $x^{:A}$ -\end_inset +\begin_inset Formula +\begin{align*} +S_{6}:\left(R\rightarrow R\right)\rightarrow Q;R\vdash R\quad(\text{axiom }Id)\quad & t_{6}(rrq,r)=r\\ +S_{2}:\left(R\rightarrow R\right)\rightarrow Q\vdash\left(R\rightarrow R\right)\quad\text{PT}_{R\rightarrow}(t_{6})\quad & t_{2}(rrq)=\left(r\rightarrow t_{6}(rrq,r)\right)\\ +S_{3}:Q\vdash Q\quad(\text{axiom }Id)\quad & t_{3}(q)=q\\ +S_{1}:\left(R\rightarrow R\right)\rightarrow Q\vdash Q\quad\text{PT}_{L\rightarrow}(t_{2},t_{3})\quad & t_{1}(rrq)=t_{3}(rrq(t_{2}(rrq)))\\ +S_{0}:\emptyset\vdash\left(\left(R\rightarrow R\right)\rightarrow Q\right)\rightarrow Q\quad\text{PT}_{R\rightarrow}(t_{1})\quad & t_{0}=\left(rrq\rightarrow t_{1}(rrq)\right) +\end{align*} -. - However, for some -\begin_inset Formula $x$ \end_inset -, the value -\begin_inset Formula $g(x)$ -\end_inset - may be of the form -\begin_inset listings -inline true -status open +\end_layout \begin_layout Plain Layout +The proof expression for +\begin_inset Formula $S_{0}$ +\end_inset -Right(c) -\end_layout + is then obtained as +\begin_inset Formula +\begin{align*} +t_{0} & =rrq\rightarrow t_{3}\left(rrq\left(t_{2}\left(rrq\right)\right)\right)=rrq\rightarrow rrq(r\rightarrow t_{6}\left(rrq,r\right)\\ + & =rrq\rightarrow rrq\left(r\rightarrow r\right) +\end{align*} \end_inset -, where -\begin_inset listings -inline true -status open +Simplified final code having the required type: +\begin_inset Formula +\[ +t_{0}:\left(\left(R\rightarrow R\right)\rightarrow Q\right)\rightarrow Q=\left(rrq\rightarrow rrq\left(r\rightarrow r\right)\right) +\] -\begin_layout Plain Layout +\end_inset -c -\end_layout -\end_inset +\end_layout - is of type -\begin_inset Formula $C$ \end_inset -. - In that case, we will have a value of type -\begin_inset Formula $C$ +To +\emph on +prove +\emph default + that there is no proof, one needs to use methods that are beyond the scope + of this book. + An introduction to the required techniques is in the book +\begin_inset Quotes eld \end_inset -, not -\begin_inset Formula $B$ +Proof and Disproof in Formal Logic +\begin_inset Quotes erd \end_inset -. - So, in general, we cannot guarantee that we can always obtain a value of - type -\begin_inset Formula $B$ + by R. +\begin_inset space ~ \end_inset - from a given value -\begin_inset Formula $x^{:A}$ -\end_inset +Bornat +\begin_inset Index idx +status open -. - This means we cannot build a function of type -\begin_inset Formula $A\rightarrow B$ -\end_inset +\begin_layout Plain Layout +Richard Bornat +\end_layout - out of the function -\begin_inset Formula $g$ \end_inset -. - Similarly, we cannot build a function of type -\begin_inset Formula $A\rightarrow C$ + (see footnote +\begin_inset space ~ \end_inset - out of -\begin_inset Formula $g$ -\end_inset -. - -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "fn:Bornat-proof-book" +plural "false" +caps "false" +noprefix "false" -\begin_layout Standard -Whether we decide to return -\begin_inset Formula $A\rightarrow B$ \end_inset - or -\begin_inset Formula $A\rightarrow C$ + on page +\begin_inset space ~ \end_inset -, we will not be able to return a value of the required type, as we just - saw. - We must conclude that we cannot implement -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -bad -\end_layout +\begin_inset CommandInset ref +LatexCommand pageref +reference "fn:Bornat-proof-book" +plural "false" +caps "false" +noprefix "false" \end_inset - as a fully parametric function. +). \end_layout -\begin_layout Standard -We could try to switch between -\begin_inset Formula $A\rightarrow B$ +\begin_layout Subsection +Failure of Boolean logic in reasoning about +\begin_inset Formula $\mathcal{CH}$ \end_inset - and -\begin_inset Formula $A\rightarrow C$ -\end_inset +-propositions +\begin_inset CommandInset label +LatexCommand label +name "subsec:Example:-Failure-of-Boolean-logic" - depending on a given value of type -\begin_inset Formula $A$ \end_inset -. - This idea, however, means that we are working with a different type signature: - -\begin_inset Formula -\[ -\forall(A,B,C).\,\left(A\rightarrow B+C\right)\rightarrow A\rightarrow\left(A\rightarrow B\right)+\left(A\rightarrow C\right)\quad. -\] -\end_inset +\end_layout -This type signature -\emph on -can -\emph default - be implemented, for instance, by this Scala code: -\begin_inset listings -inline false +\begin_layout Standard +Programmers are familiar with the +\begin_inset Index idx status open \begin_layout Plain Layout - -def q[A, B, C](g: A => Either[B, C]): A => Either[A => B, A => C] = { a - => +Boolean logic \end_layout -\begin_layout Plain Layout +\end_inset - g(a) match { -\end_layout +Boolean logic whose operations are written in Scala as +\begin_inset listings +inline true +status open \begin_layout Plain Layout - case Left(b) => Left(_ => b) +x && y \end_layout -\begin_layout Plain Layout +\end_inset - case Right(c) => Right(_ => c) -\end_layout + (conjunction), +\begin_inset listings +inline true +status open \begin_layout Plain Layout - } +x || y \end_layout +\end_inset + + (disjunction), and +\begin_inset listings +inline true +status open + \begin_layout Plain Layout -} +!x \end_layout \end_inset -But this is not the required type signature -\begin_inset space ~ -\end_inset - -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-boolean-bad-type" -plural "false" -caps "false" -noprefix "false" - + (negation). + However, it turns out that the Boolean logic does +\emph on +not +\emph default + always produce correct conclusions when reasoning about +\begin_inset Formula ${\cal CH}$ \end_inset -). +-propositions. + One needs to use the constructive logic to reason correctly about implementable + type signatures. \end_layout \begin_layout Standard -Now let us convert the type signature -\begin_inset space ~ +Let us nevertheless briefly look at how Boolean logic would handle that + reasoning. + In the Boolean logic, each proposition ( +\begin_inset Formula $\alpha$ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-boolean-bad-type" -plural "false" -caps "false" -noprefix "false" - +, +\begin_inset Formula $\beta$ \end_inset -) into a -\begin_inset Formula ${\cal CH}$ +, ...) is either +\begin_inset Formula $True$ \end_inset --proposition: -\begin_inset Formula -\begin{align} - & \forall(\alpha,\beta,\gamma).\,\left(\alpha\Rightarrow\left(\beta\vee\gamma\right)\right)\Rightarrow\left(\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)\right)\quad,\label{eq:abc-example-classical-logic-bad}\\ -\text{where we denoted}\quad & \alpha\triangleq{\cal CH}(A),\quad\beta\triangleq{\cal CH}(B),\quad\gamma\triangleq{\cal CH}(C)\quad.\nonumber -\end{align} + or +\begin_inset Formula $False$ +\end_inset +. + The operations are +\begin_inset Formula $\alpha\wedge\beta$ \end_inset -It turns out that this formula is true in Boolean logic. - To prove this, we need to show that Eq. -\begin_inset space ~ + (conjunction), +\begin_inset Formula $\alpha\vee\beta$ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:abc-example-classical-logic-bad" -plural "false" -caps "false" -noprefix "false" - + (disjunction), and +\begin_inset Formula $\neg\alpha$ \end_inset -) is equal to -\begin_inset Formula $True$ -\end_inset + (negation). + The +\series bold +implication +\series default + +\begin_inset Index idx +status open - for any Boolean values of the variables -\begin_inset Formula $\alpha$ -\end_inset +\begin_layout Plain Layout +implication (in logic) +\end_layout -, -\begin_inset Formula $\beta$ \end_inset -, -\begin_inset Formula $\gamma$ + ( +\begin_inset Formula $\Rightarrow$ \end_inset -. - One way is to rewrite the expression -\begin_inset space ~ +) is defined through other operations by: +\begin_inset Formula +\begin{equation} +\left(\alpha\Rightarrow\beta\right)\triangleq\left((\neg\alpha)\vee\beta\right)\quad.\label{eq:ch-definition-of-implication-in-Boolean-logic} +\end{equation} + \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:abc-example-classical-logic-bad" -plural "false" -caps "false" -noprefix "false" -\end_inset +\end_layout -) using the rules of Boolean logic, such as Eq. -\begin_inset space ~ +\begin_layout Standard +To verify whether a formula is true in the Boolean logic, we can substitute + either +\begin_inset Formula $True$ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-definition-of-implication-in-Boolean-logic" -plural "false" -caps "false" -noprefix "false" + or +\begin_inset Formula $False$ +\end_inset + into every variable and check if the formula has the value +\begin_inset Formula $True$ \end_inset -): -\begin_inset Formula -\begin{align*} - & \gunderline{\alpha\Rightarrow}\left(\beta\vee\gamma\right)\\ -\text{definition of }\Rightarrow\text{ via Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & \quad=(\neg\alpha)\vee\beta\vee\gamma\quad,\\ - & \gunderline{\left(\alpha\Rightarrow\beta\right)}\vee\gunderline{\left(\alpha\Rightarrow\gamma\right)}\\ -\text{definition of }\Rightarrow\text{ via Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & \quad=\gunderline{(\neg\alpha)}\vee\beta\vee\gunderline{(\neg\alpha)}\vee\gamma\\ -\text{property }x\vee x=x\text{ in Boolean logic}:\quad & \quad=(\neg\alpha)\vee\beta\vee\gamma\quad, -\end{align*} + in all possible cases. + The result can be arranged into a +\begin_inset Index idx +status open -\end_inset +\begin_layout Plain Layout +truth table +\end_layout -showing that -\begin_inset Formula $\alpha\Rightarrow(\beta\vee\gamma)$ \end_inset - is in fact -\emph on -equal -\emph default - to -\begin_inset Formula $\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)$ +truth table. + The formula is true if all values in its truth table are +\begin_inset Formula $True$ \end_inset - in Boolean logic. +. \end_layout \begin_layout Standard -Let us also give a proof via truth-value reasoning. - The only possibility for an implication -\begin_inset Formula $X\Rightarrow Y$ -\end_inset +Disjunction, conjunction, negation, and implication operations have the + following truth table: +\end_layout - to be -\begin_inset Formula $False$ -\end_inset +\begin_layout Standard +\align center - is when -\begin_inset Formula $X=True$ -\end_inset +\size small +\begin_inset Tabular + + + + + + + + + + +\begin_inset Text - and -\begin_inset Formula $Y=False$ -\end_inset +\begin_layout Plain Layout -. - So, Eq. -\begin_inset space ~ +\size small +\begin_inset Formula $\alpha$ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:abc-example-classical-logic-bad" -plural "false" -caps "false" -noprefix "false" -\end_inset +\end_layout -) can be -\begin_inset Formula $False$ \end_inset + + +\begin_inset Text - only if -\begin_inset Formula $\left(\alpha\Rightarrow(\beta\vee\gamma)\right)=True$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)=False$ +\size small +\begin_inset Formula $\beta$ \end_inset -. - A disjunction can be false only when both parts are false; so we must have - both -\begin_inset Formula $\left(\alpha\Rightarrow\beta\right)=False$ -\end_inset - and -\begin_inset Formula $\left(\alpha\Rightarrow\gamma\right)=False$ -\end_inset +\end_layout -. - This is only possible if -\begin_inset Formula $\alpha=True$ \end_inset + + +\begin_inset Text - and -\begin_inset Formula $\beta=\gamma=False$ -\end_inset +\begin_layout Plain Layout -. - But, with these value assignments, we find -\begin_inset Formula $\left(\alpha\Rightarrow(\beta\vee\gamma)\right)=False$ +\series bold +\size small +\begin_inset Formula $\alpha\vee\beta$ \end_inset - rather than -\begin_inset Formula $True$ -\end_inset - as we assumed. - It follows that we cannot ever make Eq. -\begin_inset space ~ +\end_layout + \end_inset + + +\begin_inset Text -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:abc-example-classical-logic-bad" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout +\series bold +\size small +\begin_inset Formula $\alpha\wedge\beta$ \end_inset -) equal to -\begin_inset Formula $False$ -\end_inset -. - So, Eq. -\begin_inset space ~ +\end_layout + \end_inset + + +\begin_inset Text -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:abc-example-classical-logic-bad" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout +\series bold +\size small +\begin_inset Formula $\neg\alpha$ \end_inset -) is true in Boolean logic. -\end_layout -\begin_layout Section -Equivalence of types \end_layout -\begin_layout Standard -We found a correspondence between types, code, logical propositions, and - proofs, which is known as the +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + \series bold -Curry-Howard correspondence -\series default +\size small +\begin_inset Formula $\alpha\Rightarrow\beta$ +\end_inset -\begin_inset Index idx -status open -\begin_layout Plain Layout -Curry-Howard correspondence \end_layout \end_inset + + + + +\begin_inset Text -. - An example of the CH correspondence is that a proof of the logical proposition: -\begin_inset Formula -\begin{equation} -\forall(\alpha,\beta).\,\alpha\Rightarrow\left(\beta\Rightarrow\alpha\right)\label{eq:ch-proposition-example-2} -\end{equation} - -\end_inset +\begin_layout Plain Layout -corresponds to the code of the following function: -\begin_inset listings -inline false -status open +\size small +\begin_inset Formula $True$ +\end_inset -\begin_layout Plain Layout -def f[A, B]: A => (B => A) = { x => _ => x } \end_layout \end_inset - -With the CH correspondence in mind, we may say that the -\emph on -existence -\emph default - of the code -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -x => _ => x +\size small +\begin_inset Formula $True$ +\end_inset + + \end_layout \end_inset + + +\begin_inset Text - with the type -\begin_inset Formula $A\rightarrow(B\rightarrow A)$ -\end_inset +\begin_layout Plain Layout - -\begin_inset Quotes eld +\size small +\begin_inset Formula $True$ \end_inset -is -\begin_inset Quotes erd -\end_inset - a proof of the logical formula -\begin_inset space ~ +\end_layout + \end_inset + + +\begin_inset Text -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-proposition-example-2" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout +\size small +\begin_inset Formula $True$ \end_inset -), because it shows how to compute a value of type -\begin_inset Formula $\forall(A,B).\,A\rightarrow B\rightarrow A$ -\end_inset -. \end_layout -\begin_layout Standard -The Curry-Howard correspondence maps logic formulas such as -\begin_inset Formula $(\alpha\vee\beta)\wedge\gamma$ \end_inset + + +\begin_inset Text - into type expressions such as -\begin_inset Formula $\left(A+B\right)\times C$ +\begin_layout Plain Layout + +\size small +\begin_inset Formula $False$ \end_inset -. - We have seen that types behave similarly to logic formulas in one respect: - A logic formula is a true theorem of constructive logic when the corresponding - type signature can be implemented as a fully parametric function, and vice - versa. + \end_layout -\begin_layout Standard -It turns out that the similarity ends here. - In other respects, type expressions behave as -\emph on -arithmetic -\emph default - expressions and not as logic formulas. - For this reason, the type notation used in this book denotes disjunctive - types by -\begin_inset Formula $A+B$ \end_inset + + +\begin_inset Text - and tuples by -\begin_inset Formula $A\times B$ -\end_inset +\begin_layout Plain Layout -, which is designed to remind us of arithmetic expressions (such as -\begin_inset Formula $1+2$ +\size small +\begin_inset Formula $True$ \end_inset - and -\begin_inset Formula $2\times3$ -\end_inset -) rather than of logical formulas (such as -\begin_inset Formula $A\vee B$ -\end_inset +\end_layout - and -\begin_inset Formula $A\wedge B$ \end_inset + + + + +\begin_inset Text -). - -\end_layout - -\begin_layout Standard -An important use of the type notation is for writing equations with types. - Can we use the arithmetic intuition for writing type equations such as: -\begin_inset Formula -\begin{equation} -\left(A+B\right)\times C=A\times C+B\times C\quad?\label{eq:ch-example-distributive} -\end{equation} +\begin_layout Plain Layout +\size small +\begin_inset Formula $True$ \end_inset -In this section, we will learn how to check whether one type expression - is equivalent to another. + \end_layout -\begin_layout Subsection -Logical identity does not correspond to type equivalence -\begin_inset CommandInset label -LatexCommand label -name "subsec:Logical-identity-not-type-equivalence" +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\size small +\begin_inset Formula $False$ \end_inset \end_layout -\begin_layout Standard -The CH correspondence maps Eq. -\begin_inset space ~ \end_inset + + +\begin_inset Text -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-distributive" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout +\size small +\begin_inset Formula $True$ \end_inset -) into the logic formula: -\begin_inset Formula -\begin{equation} -\forall(A,B,C).\,\left(A\vee B\right)\wedge C=\left(A\wedge C\right)\vee\left(B\wedge C\right)\quad.\label{eq:ch-example-distributive-1} -\end{equation} -\end_inset +\end_layout -This formula is the well-known -\begin_inset Quotes eld \end_inset + + +\begin_inset Text -distributive law -\begin_inset Quotes erd +\begin_layout Plain Layout + +\size small +\begin_inset Formula $False$ \end_inset -\begin_inset Foot -status open +\end_layout -\begin_layout Plain Layout -See -\family typewriter +\end_inset + + +\begin_inset Text -\begin_inset CommandInset href -LatexCommand href -target "https://en.wikipedia.org/wiki/Distributive_property#Rule_of_replacement" +\begin_layout Plain Layout +\size small +\begin_inset Formula $False$ \end_inset \end_layout \end_inset + + +\begin_inset Text - valid in Boolean logic as well as in the constructive logic. - Since a logical equation -\begin_inset Formula $P=Q$ -\end_inset - - means -\begin_inset Formula $P\Rightarrow Q$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $Q\Rightarrow P$ +\size small +\begin_inset Formula $False$ \end_inset -, the distributive law -\begin_inset space ~ -\end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-distributive-1" -plural "false" -caps "false" -noprefix "false" +\end_layout \end_inset + + + + +\begin_inset Text -) means that the two formulas hold: -\begin_inset Formula -\begin{align} - & \forall(A,B,C).\,\left(A\vee B\right)\wedge C\Rightarrow\left(A\wedge C\right)\vee\left(B\wedge C\right)\quad,\label{eq:ch-example-distributive-1a}\\ - & \forall(A,B,C).\,\left(A\wedge C\right)\vee\left(B\wedge C\right)\Rightarrow\left(A\vee B\right)\wedge C\quad.\label{eq:ch-example-distributive-1b} -\end{align} +\begin_layout Plain Layout +\size small +\begin_inset Formula $False$ \end_inset -The CH correspondence maps these logical formulas to fully parametric functions - with types: -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout -def f1[A, B, C]: ((Either[A, B], C)) => Either[(A, C), (B, C)] = ??? -\end_layout - -\begin_layout Plain Layout - -def f2[A, B, C]: Either[(A, C), (B, C)] => (Either[A, B], C) = ??? \end_layout \end_inset + + +\begin_inset Text -In the type notation, these type signatures are written as: -\begin_inset Formula -\begin{align*} - & f_{1}^{A,B,C}:\left(A+B\right)\times C\rightarrow A\times C+B\times C\quad,\\ - & f_{2}^{A,B,C}:A\times C+B\times C\rightarrow\left(A+B\right)\times C\quad. -\end{align*} +\begin_layout Plain Layout +\size small +\begin_inset Formula $True$ \end_inset -Since the two logical formulas ( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-distributive-1a" -plural "false" -caps "false" -noprefix "false" - -\end_inset -)–( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-distributive-1b" -plural "false" -caps "false" -noprefix "false" +\end_layout \end_inset - -) are true theorems in constructive logic, we expect to be able to implement - the functions -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -f1 -\end_layout - +\size small +\begin_inset Formula $True$ \end_inset - and -\begin_inset listings -inline true -status open -\begin_layout Plain Layout - -f2 \end_layout \end_inset + + +\begin_inset Text -. - It is not straightforward to guess how to combine the proof rules of Table -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Proof-rules-of-constructive-and-boolean" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout +\size small +\begin_inset Formula $False$ \end_inset - to obtain proofs of Eqs. -\begin_inset space ~ -\end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-distributive-1a" -plural "false" -caps "false" -noprefix "false" +\end_layout \end_inset + + +\begin_inset Text -)–( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-distributive-1b" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout +\size small +\begin_inset Formula $True$ \end_inset -). - So, instead of deriving the implementations of -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -f1 \end_layout \end_inset - - and -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -f2 -\end_layout - +\size small +\begin_inset Formula $True$ \end_inset - from the CH correspondence, we will write the Scala code directly. -\end_layout - -\begin_layout Standard -To implement -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -f1 \end_layout \end_inset - -, we need to perform pattern matching on the argument: -\begin_inset listings -inline false -status open + + + + +\begin_inset Text \begin_layout Plain Layout -def f1[A, B, C]: ((Either[A, B], C)) => Either[(A, C), (B, C)] = { -\end_layout +\size small +\begin_inset Formula $False$ +\end_inset -\begin_layout Plain Layout - case (Left(a), c) => Left((a, c)) // No other choice here. \end_layout -\begin_layout Plain Layout - - case (Right(b), c) => Right((b, c)) // No other choice here. -\end_layout +\end_inset + + +\begin_inset Text \begin_layout Plain Layout -} -\end_layout - +\size small +\begin_inset Formula $False$ \end_inset -In both cases, we have only one possible expression of the correct type. + \end_layout -\begin_layout Standard -Similarly, the implementation of -\begin_inset listings -inline true -status open +\end_inset + + +\begin_inset Text \begin_layout Plain Layout -f2 -\end_layout - +\size small +\begin_inset Formula $False$ \end_inset - leaves us no choices: -\end_layout -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "65col%" -status open +\end_layout -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% \end_inset - - -\begin_inset listings -inline false -status open + + +\begin_inset Text \begin_layout Plain Layout -def f2[A, B, C]: Either[(A, C), (B, C)] => (Either[A, B], C) = { -\end_layout +\size small +\begin_inset Formula $False$ +\end_inset -\begin_layout Plain Layout - case Left((a, c)) => (Left(a), c) // No other choice here. \end_layout +\end_inset + + +\begin_inset Text + \begin_layout Plain Layout - case Right((b, c)) => (Right(b), c) // No other choice here. -\end_layout +\size small +\begin_inset Formula $True$ +\end_inset -\begin_layout Plain Layout -} \end_layout \end_inset + + +\begin_inset Text +\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% +\size small +\begin_inset Formula $True$ \end_inset \end_layout +\end_inset + + + + \end_inset \end_layout \begin_layout Standard -\noindent -The code of -\begin_inset listings -inline true -status open +Using this table, we find that the formula +\begin_inset Formula $\alpha\Rightarrow\alpha$ +\end_inset -\begin_layout Plain Layout + has the value +\begin_inset Formula $True$ +\end_inset -f1 -\end_layout + in all cases, whether +\begin_inset Formula $\alpha$ +\end_inset + itself is +\begin_inset Formula $True$ \end_inset - and -\begin_inset listings -inline true -status open + or +\begin_inset Formula $False$ +\end_inset -\begin_layout Plain Layout +. + This check is sufficient to show that +\begin_inset Formula $\forall\alpha.\,\alpha\Rightarrow\alpha$ +\end_inset -f2 + is true in Boolean logic. \end_layout +\begin_layout Standard +Here is the truth table for the formulas +\begin_inset Formula $\forall(\alpha,\beta).\,(\alpha\wedge\beta)\Rightarrow\alpha$ \end_inset - never discards any given values; in other words, these functions appear - to preserve information. - We can formulate this property rigorously as a requirement that an arbitrary - value -\begin_inset listings -inline true -status open + and +\begin_inset Formula $\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\wedge\beta)$ +\end_inset -\begin_layout Plain Layout +. + The first formula is true since all values in its column are +\begin_inset Formula $True$ +\end_inset -x: (Either[A, B], C) +, while the second formula is not true since one value in the last column + is +\begin_inset Formula $False$ +\end_inset + +: \end_layout -\end_inset +\begin_layout Standard +\align center - be mapped by -\begin_inset listings -inline true -status open +\size small +\begin_inset Tabular + + + + + + + + + +\begin_inset Text \begin_layout Plain Layout -f1 -\end_layout - +\size small +\begin_inset Formula $\alpha$ \end_inset - to some value -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -y: Either[(A, C), (B, C)] \end_layout \end_inset - - and then mapped by -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -f2 -\end_layout - +\size small +\begin_inset Formula $\beta$ \end_inset - back to -\emph on -the same -\emph default - value -\begin_inset listings -inline true -status open -\begin_layout Plain Layout - -x \end_layout \end_inset - -. - Similarly, any value -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -\noindent - -y -\end_layout +\series bold +\size small +\begin_inset Formula $\alpha\wedge\beta$ \end_inset - of type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -Either[(A, C), (B, C)] \end_layout \end_inset - - should be transformed by -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -f2 -\end_layout - +\size small +\begin_inset Formula $(\alpha\wedge\beta)\Rightarrow\alpha$ \end_inset - and then by -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -f1 \end_layout \end_inset - - back to the same value -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -y -\end_layout - +\size small +\begin_inset Formula $\alpha\Rightarrow(\alpha\wedge\beta)$ \end_inset -. -\end_layout -\begin_layout Standard -Let us write these conditions as equations: -\begin_inset Formula -\[ -\forall x^{:(A+B)\times C}.\,f_{2}(f_{1}(x))=x\quad,\quad\quad\forall y^{:A\times C+B\times C}.\,f_{1}\left(f_{2}(y)\right)=y\quad. -\] +\end_layout \end_inset + + + + +\begin_inset Text -If these equations hold, it means that all the information in a value -\begin_inset Formula $x^{:(A+B)\times C}$ -\end_inset +\begin_layout Plain Layout - is completely preserved inside the value -\begin_inset Formula $y\triangleq f_{1}(x)$ +\size small +\begin_inset Formula $True$ \end_inset -; the original value -\begin_inset Formula $x$ -\end_inset - can be recovered as -\begin_inset Formula $x=f_{2}(y)$ -\end_inset +\end_layout -. - Then the function -\begin_inset Formula $f_{1}$ \end_inset - - is the -\series bold -inverse -\series default - -\begin_inset Index idx -status open + + +\begin_inset Text \begin_layout Plain Layout -inverse function -\end_layout +\size small +\begin_inset Formula $True$ \end_inset - of -\begin_inset Formula $f_{2}$ -\end_inset -. - Conversely, all the information in a value -\begin_inset Formula $y^{:A\times C+B\times C}$ -\end_inset +\end_layout - is preserved inside -\begin_inset Formula $x\triangleq f_{2}(y)$ \end_inset + + +\begin_inset Text - and can be recovered by applying -\begin_inset Formula $f_{1}$ -\end_inset +\begin_layout Plain Layout -. - Since the values -\begin_inset Formula $x^{:(A+B)\times C}$ +\size small +\begin_inset Formula $True$ \end_inset - and -\begin_inset Formula $y^{:A\times C+B\times C}$ -\end_inset - are arbitrary, it will follow that the -\emph on -data types -\emph default - themselves, -\begin_inset Formula $\left(A+B\right)\times C$ +\end_layout + \end_inset + + +\begin_inset Text - and -\begin_inset Formula $A\times C+B\times C$ +\begin_layout Plain Layout + +\size small +\begin_inset Formula $True$ \end_inset -, carry equivalent information. - Such types are called equivalent -\begin_inset Index idx -status open -\begin_layout Plain Layout -types!equivalent \end_layout \end_inset - - or isomorphic -\begin_inset Index idx -status open + + +\begin_inset Text \begin_layout Plain Layout -types!isomorphic -\end_layout +\size small +\begin_inset Formula $True$ \end_inset -\begin_inset Index idx -status open +\end_layout + +\end_inset + + + + +\begin_inset Text \begin_layout Plain Layout -isomorphic types -\end_layout +\size small +\begin_inset Formula $True$ \end_inset -. + \end_layout -\begin_layout Standard -Generally, we say that types -\begin_inset Formula $P$ \end_inset + + +\begin_inset Text - and -\begin_inset Formula $Q$ -\end_inset +\begin_layout Plain Layout - are -\series bold -equivalent -\series default - or -\series bold -isomorphic -\series default - (denoted -\begin_inset Formula $P\cong Q$ +\size small +\begin_inset Formula $False$ \end_inset -) -\begin_inset Index idx -status open -\begin_layout Plain Layout -type equivalence \end_layout \end_inset + + +\begin_inset Text -when there exist functions -\begin_inset Formula $f_{1}:P\rightarrow Q$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $f_{2}:Q\rightarrow P$ +\size small +\begin_inset Formula $False$ \end_inset - that are inverses of each other. - We can write these conditions using the notation -\begin_inset Formula $(f_{1}\bef f_{2})(x)\triangleq f_{2}(f_{1}(x))$ -\end_inset - as: -\begin_inset Formula -\[ -f_{1}\bef f_{2}=\text{id}\quad,\quad\quad f_{2}\bef f_{1}=\text{id}\quad. -\] +\end_layout \end_inset + + +\begin_inset Text -(In Scala, the forward composition -\begin_inset Formula $f_{1}\bef f_{2}$ -\end_inset +\begin_layout Plain Layout - is the function -\begin_inset listings -inline true -status open +\size small +\begin_inset Formula $True$ +\end_inset -\begin_layout Plain Layout -f1 andThen f2 \end_layout \end_inset + + +\begin_inset Text -. - We omit type annotations since we already checked that the types match.) - If these conditions hold, there is a one-to-one correspondence between - values of types -\begin_inset Formula $P$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $Q$ +\size small +\begin_inset Formula $False$ \end_inset -. - This is the same as to say that the data types -\begin_inset Formula $P$ -\end_inset - and -\begin_inset Formula $Q$ -\end_inset +\end_layout - -\begin_inset Quotes eld \end_inset + + + + +\begin_inset Text -carry equivalent information -\begin_inset Quotes erd +\begin_layout Plain Layout + +\size small +\begin_inset Formula $False$ \end_inset -. + \end_layout -\begin_layout Standard -To verify that the Scala functions -\begin_inset listings -inline true -status open +\end_inset + + +\begin_inset Text \begin_layout Plain Layout -f1 -\end_layout - +\size small +\begin_inset Formula $True$ \end_inset - and -\begin_inset listings -inline true -status open -\begin_layout Plain Layout - -f2 \end_layout \end_inset + + +\begin_inset Text - defined above are inverses of each other, we first check if -\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ -\end_inset +\begin_layout Plain Layout -. - Applying -\begin_inset Formula $f_{1}\bef f_{2}$ +\size small +\begin_inset Formula $False$ \end_inset - means to apply -\begin_inset Formula $f_{1}$ -\end_inset - and then to apply -\begin_inset Formula $f_{2}$ -\end_inset +\end_layout - to the result. - Begin by applying -\begin_inset Formula $f_{1}$ \end_inset + + +\begin_inset Text - to an arbitrary value -\begin_inset Formula $x^{:(A+B)\times C}$ -\end_inset +\begin_layout Plain Layout -. - A value -\begin_inset Formula $x$ +\size small +\begin_inset Formula $True$ \end_inset - of that type can be in only one of the two disjoint cases: a tuple -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -(Left(a), c) \end_layout \end_inset - - or a tuple -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -(Right(b), c) -\end_layout - +\size small +\begin_inset Formula $True$ \end_inset -, for some values -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -a:A \end_layout \end_inset - -, -\begin_inset listings -inline true -status open + + + + +\begin_inset Text \begin_layout Plain Layout -b:B -\end_layout - +\size small +\begin_inset Formula $False$ \end_inset -, and -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -c:C \end_layout \end_inset - -. - The Scala code of -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -f1 -\end_layout - +\size small +\begin_inset Formula $False$ \end_inset - maps these tuples to -\begin_inset listings -inline true -status open -\begin_layout Plain Layout - -Left((a, c)) \end_layout \end_inset - - and to -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -Right((b, c)) +\size small +\begin_inset Formula $False$ +\end_inset + + \end_layout \end_inset - - respectively; we can see this directly from the code of -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -f1 +\size small +\begin_inset Formula $True$ +\end_inset + + \end_layout \end_inset - -. - We then apply -\begin_inset Formula $f_{2}$ -\end_inset - - to those values, which maps them back to a tuple -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -(Left(a), c) -\end_layout - +\size small +\begin_inset Formula $True$ \end_inset - or to a tuple -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -(Right(b), c) \end_layout \end_inset + + + - respectively, according to the code of -\begin_inset listings -inline true -status open +\end_inset -\begin_layout Plain Layout -f2 \end_layout +\begin_layout Standard +Table +\begin_inset space ~ \end_inset -. - These tuples are exactly the value -\begin_inset Formula $x$ -\end_inset - we started with. - So, applying -\begin_inset Formula $f_{1}\bef f_{2}$ +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Logical-formulas-Boolean-theorems" +plural "false" +caps "false" +noprefix "false" + \end_inset - to an arbitrary -\begin_inset Formula $x^{:(A+B)\times C}$ + shows more examples of logical formulas that are true in Boolean logic. + Each formula is first written in terms of +\begin_inset Formula ${\cal CH}$ \end_inset - returns that value -\begin_inset Formula $x$ +-propositions (we denote +\begin_inset Formula $\alpha\triangleq{\cal CH}(A)$ \end_inset -. - This is the same as to say that -\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ + and +\begin_inset Formula $\beta\triangleq{\cal CH}(B)$ \end_inset -. + for brevity) and then as a Scala type signature of a function. + So, all these type signatures +\emph on +can +\emph default + be implemented. \end_layout \begin_layout Standard -To check whether -\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ -\end_inset - -, we apply -\begin_inset Formula $f_{2}$ -\end_inset - - to an arbitrary value -\begin_inset Formula $y^{:A\times C+B\times C}$ -\end_inset - -, which must be one of the two disjoint cases, -\begin_inset listings -inline true +\begin_inset Float table +placement h +wide false +sideways false status open +\begin_layout Plain Layout +\align center +\begin_inset Tabular + + + + + + + +\begin_inset Text + \begin_layout Plain Layout -Left((a, c)) +\series bold +\size small +Logic formula \end_layout \end_inset - - or -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -Right((b, c)) +\series bold +\size small +Type formula \end_layout \end_inset - -. - The code of -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -f2 +\series bold +\size small +Scala code \end_layout \end_inset - - maps these two cases into tuples -\begin_inset listings -inline true -status open + + + + +\begin_inset Text \begin_layout Plain Layout -(Left(a), c) +\size footnotesize +\begin_inset Formula $\forall\alpha.\,\alpha\Rightarrow\alpha$ +\end_inset + + \end_layout \end_inset - - and -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -(Right(b), c) +\size footnotesize +\begin_inset Formula $\forall A.\,A\rightarrow A$ +\end_inset + + \end_layout \end_inset + + +\begin_inset Text - respectively. - Then we apply +\begin_layout Plain Layout \begin_inset listings inline true status open \begin_layout Plain Layout -f1 +def id[A](x: A): A = x \end_layout \end_inset - and map these tuples back to -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -Left((a, c)) \end_layout \end_inset - - and -\begin_inset listings -inline true -status open + + + + +\begin_inset Text \begin_layout Plain Layout -Right((b, c)) +\size footnotesize +\begin_inset Formula $\forall\alpha.\,\alpha\Rightarrow True$ +\end_inset + + \end_layout \end_inset + + +\begin_inset Text - respectively. - It follows that applying -\begin_inset Formula $f_{2}$ -\end_inset +\begin_layout Plain Layout - and then -\begin_inset Formula $f_{1}$ +\size footnotesize +\begin_inset Formula $\forall A.\,A\rightarrow\bbnum 1$ \end_inset - will always return the initial value. - As a formula, this is written as -\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ -\end_inset -. \end_layout -\begin_layout Standard -By looking at the code of +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout \begin_inset listings inline true status open \begin_layout Plain Layout -f1 +def toUnit[A](x: A): Unit = () \end_layout \end_inset - and -\begin_inset listings -inline true -status open + +\end_layout + +\end_inset + + + + +\begin_inset Text \begin_layout Plain Layout -f2 +\size footnotesize +\begin_inset Formula $\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\vee\beta)$ +\end_inset + + \end_layout \end_inset - -, we can directly observe that these functions are inverses of each other: - the tuple pattern -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -(Left(a), c) +\size footnotesize +\begin_inset Formula $\forall(A,B).\,A\rightarrow A+B$ +\end_inset + + \end_layout \end_inset + + +\begin_inset Text - is mapped to +\begin_layout Plain Layout \begin_inset listings inline true status open \begin_layout Plain Layout -Left((a, c)) +def f[A, B](x: A): Either[A, B] = Left(x) \end_layout \end_inset -, and the pattern -\begin_inset listings -inline true -status open -\begin_layout Plain Layout - -(Right(b), c) \end_layout \end_inset - - to -\begin_inset listings -inline true -status open + + + + +\begin_inset Text \begin_layout Plain Layout -Right((b, c)) -\end_layout - -\end_inset - -, or vice versa. - It is visually clear that no information is lost and that the original - values are returned by function compositions -\begin_inset Formula $f_{1}\bef f_{2}$ +\size footnotesize +\begin_inset Formula $\forall(\alpha,\beta).\,(\alpha\wedge\beta)\Rightarrow\alpha$ \end_inset - or -\begin_inset Formula $f_{2}\bef f_{1}$ -\end_inset -. \end_layout -\begin_layout Standard -We find that the logical identity -\begin_inset space ~ \end_inset + + +\begin_inset Text -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-distributive-1" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout +\size footnotesize +\begin_inset Formula $\forall(A,B).\,A\times B\rightarrow A$ \end_inset -) leads to an equivalence of the corresponding types: -\begin_inset Formula -\begin{equation} -\left(A+B\right)\times C\cong A\times C+B\times C\quad.\label{eq:ch-distributive-law-types} -\end{equation} -\end_inset +\end_layout -To get Eq. -\begin_inset space ~ \end_inset + + +\begin_inset Text -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-distributive-law-types" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout +\begin_inset listings +inline true +status open -\end_inset +\begin_layout Plain Layout + +def f[A, B](p: (A, B)): A = p._1 +\end_layout -) from Eq. -\begin_inset space ~ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-distributive-1" -plural "false" -caps "false" -noprefix "false" -\end_inset +\end_layout -), we need to convert a logical formula to an arithmetic expression by replacing - the disjunction operations -\begin_inset Formula $\vee$ \end_inset + + + + +\begin_inset Text - by -\begin_inset Formula $+$ -\end_inset +\begin_layout Plain Layout - and the conjunctions -\begin_inset Formula $\wedge$ +\size footnotesize +\begin_inset Formula $\forall(\alpha,\beta).\,\alpha\Rightarrow(\beta\Rightarrow\alpha)$ \end_inset - by -\begin_inset Formula $\times$ -\end_inset - everywhere. \end_layout -\begin_layout Standard -As another example of a logical identity, consider the associativity law - for conjunction: -\begin_inset Formula -\begin{equation} -\left(\alpha\wedge\beta\right)\wedge\gamma=\alpha\wedge\left(\beta\wedge\gamma\right)\quad.\label{eq:ch-example-associativity-conjunction} -\end{equation} - \end_inset + + +\begin_inset Text -The corresponding types are -\begin_inset Formula $(A\times B)\times C$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $A\times(B\times C)$ +\size footnotesize +\begin_inset Formula $\forall(A,B).\,A\rightarrow(B\rightarrow A)$ \end_inset -; in Scala, -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -((A, B), C) \end_layout \end_inset + + +\begin_inset Text - and +\begin_layout Plain Layout \begin_inset listings inline true status open \begin_layout Plain Layout -(A, (B, C)) +def f[A, B](x: A): B => A = (_ => x) \end_layout \end_inset -. - We can define functions that convert between these types without information - loss -\begin_inset Index idx -status open -\begin_layout Plain Layout -information loss \end_layout \end_inset + + + -: -\begin_inset listings -inline false -status open +\end_inset -\begin_layout Plain Layout -def f3[A, B, C]: (((A, B), C)) => (A, (B, C)) = { case ((a, b), c) => (a, - (b, c)) } \end_layout \begin_layout Plain Layout +\begin_inset Caption Standard -def f4[A, B, C]: (A, (B, C)) => (((A, B), C)) = { case (a, (b, c)) => ((a, - b), c) } -\end_layout +\begin_layout Plain Layout +Examples of logical formulas that are true theorems in Boolean logic. +\begin_inset CommandInset label +LatexCommand label +name "tab:Logical-formulas-Boolean-theorems" \end_inset -By applying these functions to arbitrary values of types -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -((A, B), C) \end_layout \end_inset - and -\begin_inset listings -inline true -status open -\begin_layout Plain Layout - -(A, (B, C)) \end_layout \end_inset -, it is easy to see that the functions -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -f3 \end_layout +\begin_layout Standard +Table +\begin_inset space ~ \end_inset - and -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -f4 -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Logical-formulas-not-Boolean-theorems" +plural "false" +caps "false" +noprefix "false" \end_inset - are inverses of each other. - This is also directly visible in the code: the nested tuple pattern -\begin_inset listings -inline true + shows some examples of formulas that are +\emph on +not true +\emph default + in Boolean logic. + Translated into type formulas and then into Scala, these formulas yield + type signatures that +\emph on +cannot +\emph default + be implemented by fully parametric functions. +\end_layout + +\begin_layout Standard +\begin_inset Float table +placement h +wide false +sideways false status open +\begin_layout Plain Layout +\align center +\begin_inset Tabular + + + + + + + +\begin_inset Text + \begin_layout Plain Layout -((a, b), c) +\series bold +\size small +Logic formula \end_layout \end_inset - - is mapped to the pattern -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -(a, (b, c)) +\series bold +\size small +Type formula \end_layout \end_inset + + +\begin_inset Text - and back. - So, the types -\begin_inset Formula $\left(A\times B\right)\times C$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $A\times\left(B\times C\right)$ -\end_inset - - are equivalent, and we can write -\begin_inset Formula $A\times B\times C$ -\end_inset - - without parentheses. +\series bold +\size small +Scala type signature \end_layout -\begin_layout Standard -Does a logical identity always correspond to an equivalence of types? This - turns out to be -\emph on -not -\emph default - so. - A simple example of a logical identity that does not correspond to a type - equivalence is: -\begin_inset Formula -\begin{equation} -True\vee\alpha=True\quad.\label{eq:ch-example-logic-identity-2} -\end{equation} - \end_inset + + + + +\begin_inset Text -Since the CH correspondence maps the logical constant -\begin_inset Formula $True$ -\end_inset +\begin_layout Plain Layout - into the unit type -\begin_inset Formula $\bbnum 1$ +\size footnotesize +\begin_inset Formula $\forall\alpha.\,True\Rightarrow\alpha$ \end_inset -, the type equivalence corresponding to Eq. -\begin_inset space ~ -\end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-logic-identity-2" -plural "false" -caps "false" -noprefix "false" +\end_layout \end_inset + + +\begin_inset Text -) is -\begin_inset Formula $\bbnum 1+A\cong\bbnum 1$ -\end_inset +\begin_layout Plain Layout -. - The type denoted by -\begin_inset Formula $\bbnum 1+A$ +\size footnotesize +\begin_inset Formula $\forall A.\,\bbnum 1\rightarrow A$ \end_inset - means -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -Option[A] \end_layout \end_inset + + +\begin_inset Text - in Scala, so the corresponding equivalence is +\begin_layout Plain Layout \begin_inset listings inline true status open \begin_layout Plain Layout -Option[A] +def f[A](x: Unit): A \end_layout \end_inset -\begin_inset Formula $\cong$ +\end_layout + \end_inset + + + + +\begin_inset Text +\begin_layout Plain Layout -\begin_inset listings -inline true -status open +\size footnotesize +\begin_inset Formula $\forall(\alpha,\beta).\,(\alpha\vee\beta)\Rightarrow\alpha$ +\end_inset -\begin_layout Plain Layout -Unit \end_layout \end_inset - -. - Intuitively, this type equivalence should not hold: an -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -Option[A] -\end_layout - +\size footnotesize +\begin_inset Formula $\forall(A,B).\,A+B\rightarrow A$ \end_inset - may carry a value of type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -A \end_layout \end_inset + + +\begin_inset Text -, which cannot possibly be stored in a value of type +\begin_layout Plain Layout \begin_inset listings inline true status open \begin_layout Plain Layout -Unit +def f[A,B](x: Either[A, B]): A \end_layout \end_inset -. - We can verify this intuition rigorously by proving that any fully parametric - functions with type signatures -\begin_inset Formula $g_{1}:\bbnum 1+A\rightarrow\bbnum 1$ -\end_inset - and -\begin_inset Formula $g_{2}:\bbnum 1\rightarrow\bbnum 1+A$ -\end_inset +\end_layout - will not satisfy -\begin_inset Formula $g_{1}\bef g_{2}=\text{id}$ \end_inset + + + + +\begin_inset Text -. - To verify this, we note that -\begin_inset Formula $g_{2}:\bbnum 1\rightarrow\bbnum 1+A$ +\begin_layout Plain Layout + +\size footnotesize +\begin_inset Formula $\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\wedge\beta)$ \end_inset - must have this type signature: + \end_layout -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open +\end_inset + + +\begin_inset Text \begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% + +\size footnotesize +\begin_inset Formula $\forall(A,B).\,A\rightarrow A\times B$ \end_inset +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def g2[A]: Unit => Option[A] = ??? +def f[A,B](p: A): (A, B) \end_layout \end_inset @@ -13094,1647 +13175,1796 @@ def g2[A]: Unit => Option[A] = ??? \end_layout -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% \end_inset + + + + +\begin_inset Text +\begin_layout Plain Layout -\end_layout - +\size footnotesize +\begin_inset Formula $\forall(\alpha,\beta).\,(\alpha\Rightarrow\beta)\Rightarrow\alpha$ \end_inset \end_layout -\begin_layout Standard -\noindent -This function must always return -\begin_inset listings -inline true -status open +\end_inset + + +\begin_inset Text \begin_layout Plain Layout -None -\end_layout - +\size footnotesize +\begin_inset Formula $\forall(A,B).\,(A\rightarrow B)\rightarrow A$ \end_inset -, since a fully parametric function cannot produce values of an arbitrary - type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -A \end_layout \end_inset + + +\begin_inset Text - from scratch. - Therefore, -\begin_inset Formula $g_{1}\bef g_{2}$ -\end_inset - - is also a function that always returns +\begin_layout Plain Layout \begin_inset listings inline true status open \begin_layout Plain Layout -None +def f[A,B](x: A => B): A \end_layout \end_inset -. - The function -\begin_inset Formula $g_{1}\bef g_{2}$ -\end_inset - has type signature -\begin_inset Formula $\bbnum 1+A\rightarrow\bbnum 1+A$ +\end_layout + \end_inset + + + - or, in Scala syntax, -\begin_inset listings -inline true -status open +\end_inset -\begin_layout Plain Layout -Option[A] => Option[A] \end_layout -\end_inset +\begin_layout Plain Layout +\begin_inset Caption Standard -, and is not equal to the identity function, because the identity function - does +\begin_layout Plain Layout +Examples of logical formulas that are \emph on not \emph default - always return -\begin_inset listings -inline true -status open + true in Boolean logic. +\begin_inset CommandInset label +LatexCommand label +name "tab:Logical-formulas-not-Boolean-theorems" + +\end_inset -\begin_layout Plain Layout -None \end_layout \end_inset -. + \end_layout -\begin_layout Standard -Another example of a logical identity without a type equivalence is the - distributive law: +\end_inset + + +\end_layout + +\begin_layout Standard +At first sight, it may appear from these examples that whenever a logical + formula is true in Boolean logic, the corresponding type signature can + be implemented in code, and vice versa. + However, this is +\emph on +incorrect +\emph default +: the rules of Boolean logic are not fully suitable for reasoning about + types in a functional language. + We will now show some examples of formulas that are true in Boolean logic + but correspond to unimplementable type signatures. +\end_layout + +\begin_layout Standard +The first example is given by the following type: \begin_inset Formula \begin{equation} -\forall(A,B,C).\,\left(A\wedge B\right)\vee C=\left(A\vee C\right)\wedge\left(B\vee C\right)\quad,\label{eq:ch-example-distributive-2} +\forall(A,B,C).\,\left(A\rightarrow B+C\right)\rightarrow\left(A\rightarrow B\right)+\left(A\rightarrow C\right)\quad,\label{eq:ch-example-boolean-bad-type} \end{equation} \end_inset -which is -\begin_inset Quotes eld -\end_inset - -dual -\begin_inset Quotes erd -\end_inset - - to the law +which corresponds to the Scala type signature (shown in Section \begin_inset space ~ \end_inset -( + \begin_inset CommandInset ref LatexCommand ref -reference "eq:ch-example-distributive-1" +reference "subsec:Motivation-and-outlook" plural "false" caps "false" noprefix "false" \end_inset -), i.e., it is obtained from Eq. -\begin_inset space ~ -\end_inset +): +\begin_inset listings +inline false +status open -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-distributive-1" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout -\end_inset +def bad2[A, B, C](g: A => Either[B, C]): Either[A => B, A => C] = ??? +\end_layout -) by swapping all conjunctions ( -\begin_inset Formula $\wedge$ \end_inset -) with disjunctions ( -\begin_inset Formula $\vee$ -\end_inset +The function +\begin_inset listings +inline true +status open -). - In logic, a dual formula to an identity is also an identity. - The CH correspondence maps Eq. -\begin_inset space ~ -\end_inset +\begin_layout Plain Layout -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-distributive-2" -plural "false" -caps "false" -noprefix "false" +bad2 +\end_layout \end_inset -) into the type equation: -\begin_inset Formula -\begin{equation} -\forall(A,B,C).\,\left(A\times B\right)+C=\left(A+C\right)\times\left(B+C\right)\quad.\label{eq:ch-example-incorrect-identity-2} -\end{equation} - + +\emph on +cannot +\emph default + be implemented via fully parametric code. + To see why, consider that the only available data is a function +\begin_inset Formula $g^{:A\rightarrow B+C}$ \end_inset -However, the types -\begin_inset Formula $A\times B+C$ +, which returns values of type +\begin_inset Formula $B$ \end_inset - and -\begin_inset Formula $\left(A+C\right)\times\left(B+C\right)$ + or +\begin_inset Formula $C$ \end_inset - are -\emph on -not -\emph default - equivalent. - To see why, look at the possible code of a function -\begin_inset Formula $g_{3}:\left(A+C\right)\times\left(B+C\right)\rightarrow A\times B+C$ + depending (in some unknown way) on the input value of type +\begin_inset Formula $A$ \end_inset -: +. + The function \begin_inset listings -lstparams "numbers=left" -inline false +inline true status open \begin_layout Plain Layout -def g3[A,B,C]: ((Either[A, C], Either[B, C])) => Either[(A, B), C] = { -\end_layout - -\begin_layout Plain Layout - - case (Left(a), Left(b)) => Left((a, b)) // No other choice. +bad2 \end_layout -\begin_layout Plain Layout +\end_inset - case (Left(a), Right(c)) => Right(c) // No other choice. -\end_layout + must return either a function of type +\begin_inset Formula $A\rightarrow B$ +\end_inset -\begin_layout Plain Layout + or a function of type +\begin_inset Formula $A\rightarrow C$ +\end_inset - case (Right(c), Left(b)) => Right(c) // No other choice. -\end_layout +. + How can the code of +\begin_inset listings +inline true +status open \begin_layout Plain Layout - case (Right(c1), Right(c2)) => Right(c1) // Must discard c1 or c2 - here! +bad2 \end_layout -\begin_layout Plain Layout +\end_inset -} // May return Right(c2) instead of Right(c1) in the last line. -\end_layout + make that decision? The only input data is the function +\begin_inset Formula $g$ +\end_inset + that takes an argument of type +\begin_inset Formula $A$ \end_inset -In line 5, we have a choice of returning -\begin_inset listings -inline true -status open +. + We could imagine applying +\begin_inset Formula $g$ +\end_inset -\begin_layout Plain Layout + to various arguments of type +\begin_inset Formula $A$ +\end_inset -Right(c1) -\end_layout + and to see whether +\begin_inset Formula $g$ +\end_inset + returns a +\begin_inset Formula $B$ \end_inset - or -\begin_inset listings -inline true -status open + or a +\begin_inset Formula $C$ +\end_inset -\begin_layout Plain Layout +. + However, the type +\begin_inset Formula $A$ +\end_inset -Right(c2) -\end_layout + is arbitrary, and a fully parametric function cannot produce a value of + type +\begin_inset Formula $A$ +\end_inset + in order to apply +\begin_inset Formula $g$ \end_inset -. - Whichever we choose, we will lose information -\begin_inset Index idx -status open + to it. + So, the decision about whether to return +\begin_inset Formula $A\rightarrow B$ +\end_inset -\begin_layout Plain Layout -information loss -\end_layout + or +\begin_inset Formula $A\rightarrow C$ +\end_inset + must be independent of +\begin_inset Formula $g$ \end_inset - because we will have discarded one of the given values +. + That decision must be hard-coded in the function \begin_inset listings inline true status open \begin_layout Plain Layout -c1 +bad2 \end_layout \end_inset -, -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -c2 +. \end_layout +\begin_layout Standard +Suppose we hard-coded the decision to return a function of type +\begin_inset Formula $A\rightarrow B$ \end_inset . - After evaluating -\begin_inset Formula $g_{3}$ + How would we create a function of type +\begin_inset Formula $A\rightarrow B$ \end_inset -, we will not be able to restore -\emph on -both -\emph default - + in the body of \begin_inset listings inline true status open \begin_layout Plain Layout -c1 +bad2 \end_layout \end_inset - and -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -c2 -\end_layout - +? Given a value +\begin_inset Formula $x^{:A}$ \end_inset -, no matter what code we write. - So, the composition -\begin_inset Formula $g_{3}\bef g_{4}$ + of type +\begin_inset Formula $A$ \end_inset - with any -\begin_inset Formula $g_{4}$ +, we would need to compute some value of type +\begin_inset Formula $B$ \end_inset - cannot be equal to the identity function. - The type equation -\begin_inset space ~ +. + Since the type +\begin_inset Formula $B$ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-incorrect-identity-2" -plural "false" -caps "false" -noprefix "false" - + is arbitrary (it is a type parameter), we cannot produce a value of type + +\begin_inset Formula $B$ \end_inset -) is incorrect. -\end_layout + from scratch. + The only potential source of values of type +\begin_inset Formula $B$ +\end_inset -\begin_layout Standard -We find that a logical identity -\begin_inset Formula ${\cal CH}(P)={\cal CH}(Q)$ + is the given function +\begin_inset Formula $g$ \end_inset - guarantees, via the CH correspondence, that we can implement -\emph on -some -\emph default - fully parametric functions of types -\begin_inset Formula $P\rightarrow Q$ +. + The only way of using +\begin_inset Formula $g$ \end_inset - and -\begin_inset Formula $Q\rightarrow P$ + is to apply it to +\begin_inset Formula $x^{:A}$ \end_inset . - However, it is not guaranteed that these functions are inverses of each - other, i.e., that the type conversions -\begin_inset Formula $P\rightarrow Q$ + However, for some +\begin_inset Formula $x$ \end_inset - or -\begin_inset Formula $Q\rightarrow P$ +, the value +\begin_inset Formula $g(x)$ \end_inset - have no information loss -\begin_inset Index idx + may be of the form +\begin_inset listings +inline true status open \begin_layout Plain Layout -information loss + +Right(c) +\end_layout + +\end_inset + +, where +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +c \end_layout +\end_inset + + is of type +\begin_inset Formula $C$ \end_inset . - So, the type equivalence -\begin_inset Formula $P\cong Q$ + In that case, we will have a value of type +\begin_inset Formula $C$ \end_inset - does not automatically follow from the logical identity -\begin_inset Formula ${\cal CH}(P)={\cal CH}(Q)$ +, not +\begin_inset Formula $B$ \end_inset . -\end_layout + So, in general, we cannot guarantee that we can always obtain a value of + type +\begin_inset Formula $B$ +\end_inset -\begin_layout Standard -The CH correspondence means that for true propositions -\begin_inset Formula ${\cal CH}(X)$ + from a given value +\begin_inset Formula $x^{:A}$ \end_inset - we can compute -\emph on -some -\emph default - value -\begin_inset Formula $x$ +. + This means we cannot build a function of type +\begin_inset Formula $A\rightarrow B$ \end_inset - of type -\begin_inset Formula $X$ + out of the function +\begin_inset Formula $g$ \end_inset . - However, the CH correspondence does not guarantee that the computed value - -\begin_inset Formula $x^{:X}$ + Similarly, we cannot build a function of type +\begin_inset Formula $A\rightarrow C$ \end_inset - will satisfy any additional properties or laws. -\end_layout + out of +\begin_inset Formula $g$ +\end_inset -\begin_layout Subsection -Arithmetic identity corresponds to type equivalence +. + \end_layout \begin_layout Standard -Looking at the examples of equivalent types, we notice that correct type - equivalences correspond to -\emph on -arithmetical -\emph default - identities rather than -\emph on -logical -\emph default - identities. - For instance, the logical identity in Eq. -\begin_inset space ~ +Whether we decide to return +\begin_inset Formula $A\rightarrow B$ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-distributive-1" -plural "false" -caps "false" -noprefix "false" + or +\begin_inset Formula $A\rightarrow C$ +\end_inset + +, we will not be able to return a value of the required type, as we just + saw. + We must conclude that we cannot implement +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +bad +\end_layout \end_inset -) leads to the type equivalence -\begin_inset space ~ + as a fully parametric function. +\end_layout + +\begin_layout Standard +We could try to switch between +\begin_inset Formula $A\rightarrow B$ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-distributive-law-types" -plural "false" -caps "false" -noprefix "false" + and +\begin_inset Formula $A\rightarrow C$ +\end_inset + depending on a given value of type +\begin_inset Formula $A$ \end_inset -), which looks like a standard identity of arithmetic, such as: +. + This idea, however, means that we are working with a different type signature: + \begin_inset Formula \[ -(1+10)\times20=1\times20+10\times20\quad. +\forall(A,B,C).\,\left(A\rightarrow B+C\right)\rightarrow A\rightarrow\left(A\rightarrow B\right)+\left(A\rightarrow C\right)\quad. \] \end_inset -The logical identity in Eq. +This type signature +\emph on +can +\emph default + be implemented, for instance, by this Scala code: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def q[A, B, C](g: A => Either[B, C]): A => Either[A => B, A => C] = { a + => +\end_layout + +\begin_layout Plain Layout + + g(a) match { +\end_layout + +\begin_layout Plain Layout + + case Left(b) => Left(_ => b) +\end_layout + +\begin_layout Plain Layout + + case Right(c) => Right(_ => c) +\end_layout + +\begin_layout Plain Layout + + } +\end_layout + +\begin_layout Plain Layout + +} +\end_layout + +\end_inset + +But this is not the required type signature \begin_inset space ~ \end_inset ( \begin_inset CommandInset ref LatexCommand ref -reference "eq:ch-example-distributive-2" +reference "eq:ch-example-boolean-bad-type" plural "false" caps "false" noprefix "false" \end_inset -), which does -\emph on -not -\emph default - yield a type equivalence, leads to an incorrect arithmetic equation +). +\end_layout + +\begin_layout Standard +Now let us convert the type signature \begin_inset space ~ \end_inset - +( \begin_inset CommandInset ref LatexCommand ref -reference "eq:ch-example-incorrect-identity-2" +reference "eq:ch-example-boolean-bad-type" plural "false" caps "false" noprefix "false" \end_inset -, e.g., -\begin_inset Formula $\left(1\times10\right)+20\neq\left(1+20\right)\times\left(10+20\right)$ +) into a +\begin_inset Formula ${\cal CH}$ \end_inset -. - Similarly, the associativity law +-proposition: +\begin_inset Formula +\begin{align} + & \forall(\alpha,\beta,\gamma).\,\left(\alpha\Rightarrow\left(\beta\vee\gamma\right)\right)\Rightarrow\left(\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)\right)\quad,\label{eq:abc-example-classical-logic-bad}\\ +\text{where we denoted}\quad & \alpha\triangleq{\cal CH}(A),\quad\beta\triangleq{\cal CH}(B),\quad\gamma\triangleq{\cal CH}(C)\quad.\nonumber +\end{align} + +\end_inset + +It turns out that this formula is true in Boolean logic. + To prove this, we need to show that Eq. \begin_inset space ~ \end_inset ( \begin_inset CommandInset ref LatexCommand ref -reference "eq:ch-example-associativity-conjunction" +reference "eq:abc-example-classical-logic-bad" plural "false" caps "false" noprefix "false" \end_inset -) leads to a type equivalence and to the arithmetic identity: -\begin_inset Formula -\[ -\left(a\times b\right)\times c=a\times\left(b\times c\right)\quad, -\] +) is equal to +\begin_inset Formula $True$ +\end_inset + for any Boolean values of the variables +\begin_inset Formula $\alpha$ \end_inset -The logical identity in Eq. +, +\begin_inset Formula $\beta$ +\end_inset + +, +\begin_inset Formula $\gamma$ +\end_inset + +. + One way is to rewrite the expression \begin_inset space ~ \end_inset ( \begin_inset CommandInset ref LatexCommand ref -reference "eq:ch-example-logic-identity-2" +reference "eq:abc-example-classical-logic-bad" plural "false" caps "false" noprefix "false" \end_inset -), which does not yield a type equivalence, leads to an incorrect arithmetic - statement ( -\begin_inset Formula $\forall a.\,1+a=1$ -\end_inset - -). -\end_layout - -\begin_layout Standard -Table +) using the rules of Boolean logic, such as Eq. \begin_inset space ~ \end_inset - +( \begin_inset CommandInset ref LatexCommand ref -reference "tab:Logical-identities-with-disjunction-and-conjunction" +reference "eq:ch-definition-of-implication-in-Boolean-logic" plural "false" caps "false" noprefix "false" \end_inset - summarizes these and other examples of logical identities and the corresponding - type equivalences. - In all rows, quantifiers such as -\begin_inset Formula $\forall\alpha$ -\end_inset +): +\begin_inset Formula +\begin{align*} + & \gunderline{\alpha\Rightarrow}\left(\beta\vee\gamma\right)\\ +\text{definition of }\Rightarrow\text{ via Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & \quad=(\neg\alpha)\vee\beta\vee\gamma\quad,\\ + & \gunderline{\left(\alpha\Rightarrow\beta\right)}\vee\gunderline{\left(\alpha\Rightarrow\gamma\right)}\\ +\text{definition of }\Rightarrow\text{ via Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & \quad=\gunderline{(\neg\alpha)}\vee\beta\vee\gunderline{(\neg\alpha)}\vee\gamma\\ +\text{property }x\vee x=x\text{ in Boolean logic}:\quad & \quad=(\neg\alpha)\vee\beta\vee\gamma\quad, +\end{align*} - or -\begin_inset Formula $\forall(A,B)$ \end_inset - are implied as necessary. -\end_layout - -\begin_layout Standard -Because we chose the type notation to be similar to the ordinary arithmetic - notation, it is easy to translate a possible type equivalence into an arithmeti -c equation. - In all cases, valid arithmetic identities correspond to type equivalences, - and failures to obtain a type equivalence correspond to incorrect arithmetic - identities. - With regard to type equivalence, types such as -\begin_inset Formula $A+B$ +showing that +\begin_inset Formula $\alpha\Rightarrow(\beta\vee\gamma)$ \end_inset - and -\begin_inset Formula $A\times B$ + is in fact +\emph on +equal +\emph default + to +\begin_inset Formula $\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)$ \end_inset - behave similarly to arithmetic expressions such as -\begin_inset Formula $10+20$ + in Boolean logic. +\end_layout + +\begin_layout Standard +Let us also give a proof via truth-value reasoning. + The only possibility for an implication +\begin_inset Formula $X\Rightarrow Y$ \end_inset - and -\begin_inset Formula $10\times20$ + to be +\begin_inset Formula $False$ \end_inset - and not similarly to logical formulas such as -\begin_inset Formula $\alpha\vee\beta$ + is when +\begin_inset Formula $X=True$ \end_inset and -\begin_inset Formula $\alpha\wedge\beta$ +\begin_inset Formula $Y=False$ \end_inset . -\end_layout - -\begin_layout Standard -\begin_inset Float table -wide false -sideways false -status open + So, Eq. +\begin_inset space ~ +\end_inset -\begin_layout Plain Layout -\align center -\begin_inset Tabular - - - - - - -\begin_inset Text +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:abc-example-classical-logic-bad" +plural "false" +caps "false" +noprefix "false" -\begin_layout Plain Layout +\end_inset -\series bold -\size small -Logical identity -\end_layout +) can be +\begin_inset Formula $False$ +\end_inset + only if +\begin_inset Formula $\left(\alpha\Rightarrow(\beta\vee\gamma)\right)=True$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + and +\begin_inset Formula $\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)=False$ +\end_inset -\series bold -\size small -Type equivalence (if it holds) -\end_layout +. + A disjunction can be false only when both parts are false; so we must have + both +\begin_inset Formula $\left(\alpha\Rightarrow\beta\right)=False$ +\end_inset + and +\begin_inset Formula $\left(\alpha\Rightarrow\gamma\right)=False$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout +. + This is only possible if +\begin_inset Formula $\alpha=True$ +\end_inset -\size small -\begin_inset Formula $True\vee\alpha=True$ + and +\begin_inset Formula $\beta=\gamma=False$ \end_inset +. + But, with these value assignments, we find +\begin_inset Formula $\left(\alpha\Rightarrow(\beta\vee\gamma)\right)=False$ +\end_inset -\end_layout + rather than +\begin_inset Formula $True$ +\end_inset + as we assumed. + It follows that we cannot ever make Eq. +\begin_inset space ~ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:abc-example-classical-logic-bad" +plural "false" +caps "false" +noprefix "false" -\size small -\begin_inset Formula $\bbnum 1+A\not\cong\bbnum 1$ \end_inset +) equal to +\begin_inset Formula $False$ +\end_inset -\end_layout - +. + So, Eq. +\begin_inset space ~ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:abc-example-classical-logic-bad" +plural "false" +caps "false" +noprefix "false" -\size small -\begin_inset Formula $True\wedge\alpha=\alpha$ \end_inset - +) is true in Boolean logic. \end_layout -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout +\begin_layout Section +Equivalence of types +\end_layout -\size small -\begin_inset Formula $\bbnum 1\times A\cong A$ -\end_inset +\begin_layout Standard +We found a correspondence between types, code, logical propositions, and + proofs, which is known as the +\series bold +Curry-Howard correspondence +\series default +\begin_inset Index idx +status open +\begin_layout Plain Layout +Curry-Howard correspondence \end_layout \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout +. + An example of the CH correspondence is that a proof of the logical proposition: +\begin_inset Formula +\begin{equation} +\forall(\alpha,\beta).\,\alpha\Rightarrow\left(\beta\Rightarrow\alpha\right)\label{eq:ch-proposition-example-2} +\end{equation} -\size small -\begin_inset Formula $False\vee\alpha=\alpha$ \end_inset +corresponds to the code of the following function: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout +def f[A, B]: A => (B => A) = { x => _ => x } \end_layout \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $\bbnum 0+A\cong A$ -\end_inset +With the CH correspondence in mind, we may say that the +\emph on +existence +\emph default + of the code +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +x => _ => x \end_layout \end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $False\wedge\alpha=False$ + with the type +\begin_inset Formula $A\rightarrow(B\rightarrow A)$ \end_inset + +\begin_inset Quotes eld +\end_inset -\end_layout +is +\begin_inset Quotes erd +\end_inset + a proof of the logical formula +\begin_inset space ~ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-proposition-example-2" +plural "false" +caps "false" +noprefix "false" -\size small -\begin_inset Formula $\bbnum 0\times A\cong\bbnum 0$ \end_inset +), because it shows how to compute a value of type +\begin_inset Formula $\forall(A,B).\,A\rightarrow B\rightarrow A$ +\end_inset +. \end_layout +\begin_layout Standard +The Curry-Howard correspondence maps logic formulas such as +\begin_inset Formula $(\alpha\vee\beta)\wedge\gamma$ \end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $\alpha\vee\beta=\beta\vee\alpha$ + into type expressions such as +\begin_inset Formula $\left(A+B\right)\times C$ \end_inset - +. + We have seen that types behave similarly to logic formulas in one respect: + A logic formula is a true theorem of constructive logic when the corresponding + type signature can be implemented as a fully parametric function, and vice + versa. \end_layout +\begin_layout Standard +It turns out that the similarity ends here. + In other respects, type expressions behave as +\emph on +arithmetic +\emph default + expressions and not as logic formulas. + For this reason, the type notation used in this book denotes disjunctive + types by +\begin_inset Formula $A+B$ \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $A+B\cong B+A$ + and tuples by +\begin_inset Formula $A\times B$ \end_inset - -\end_layout - +, which is designed to remind us of arithmetic expressions (such as +\begin_inset Formula $1+2$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout + and +\begin_inset Formula $2\times3$ +\end_inset -\size small -\begin_inset Formula $\alpha\wedge\beta=\beta\wedge\alpha$ +) rather than of logical formulas (such as +\begin_inset Formula $A\vee B$ \end_inset + and +\begin_inset Formula $A\wedge B$ +\end_inset +). + \end_layout +\begin_layout Standard +An important use of the type notation is for writing equations with types. + Can we use the arithmetic intuition for writing type equations such as: +\begin_inset Formula +\begin{equation} +\left(A+B\right)\times C=A\times C+B\times C\quad?\label{eq:ch-example-distributive} +\end{equation} + \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +In this section, we will learn how to check whether one type expression + is equivalent to another. +\end_layout + +\begin_layout Subsection +Logical identity does not correspond to type equivalence +\begin_inset CommandInset label +LatexCommand label +name "subsec:Logical-identity-not-type-equivalence" -\size small -\begin_inset Formula $A\times B\cong B\times A$ \end_inset \end_layout +\begin_layout Standard +The CH correspondence maps Eq. +\begin_inset space ~ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive" +plural "false" +caps "false" +noprefix "false" -\size small -\begin_inset Formula $\left(\alpha\vee\beta\right)\vee\gamma=\alpha\vee\left(\beta\vee\gamma\right)$ \end_inset - -\end_layout +) into the logic formula: +\begin_inset Formula +\begin{equation} +\forall(A,B,C).\,\left(A\vee B\right)\wedge C=\left(A\wedge C\right)\vee\left(B\wedge C\right)\quad.\label{eq:ch-example-distributive-1} +\end{equation} \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $\left(A+B\right)+C\cong A+\left(B+C\right)$ +This formula is the well-known +\begin_inset Quotes eld \end_inset +distributive law +\begin_inset Quotes erd +\end_inset -\end_layout -\end_inset - - - - -\begin_inset Text +\begin_inset Foot +status open \begin_layout Plain Layout +See +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "https://en.wikipedia.org/wiki/Distributive_property#Rule_of_replacement" -\size small -\begin_inset Formula $\left(\alpha\wedge\beta\right)\wedge\gamma=\alpha\wedge\left(\beta\wedge\gamma\right)$ \end_inset \end_layout \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $\left(A\times B\right)\times C\cong A\times\left(B\times C\right)$ + valid in Boolean logic as well as in the constructive logic. + Since a logical equation +\begin_inset Formula $P=Q$ \end_inset + means +\begin_inset Formula $P\Rightarrow Q$ +\end_inset -\end_layout + and +\begin_inset Formula $Q\Rightarrow P$ +\end_inset +, the distributive law +\begin_inset space ~ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive-1" +plural "false" +caps "false" +noprefix "false" -\size small -\begin_inset Formula $\left(\alpha\vee\beta\right)\wedge\gamma=\left(\alpha\wedge\gamma\right)\vee\left(\beta\wedge\gamma\right)$ \end_inset - -\end_layout +) means that the two formulas hold: +\begin_inset Formula +\begin{align} + & \forall(A,B,C).\,\left(A\vee B\right)\wedge C\Rightarrow\left(A\wedge C\right)\vee\left(B\wedge C\right)\quad,\label{eq:ch-example-distributive-1a}\\ + & \forall(A,B,C).\,\left(A\wedge C\right)\vee\left(B\wedge C\right)\Rightarrow\left(A\vee B\right)\wedge C\quad.\label{eq:ch-example-distributive-1b} +\end{align} \end_inset - - -\begin_inset Text + +The CH correspondence maps these logical formulas to fully parametric functions + with types: +\begin_inset listings +inline false +status open \begin_layout Plain Layout -\size small -\begin_inset Formula $\left(A+B\right)\times C\cong A\times C+B\times C$ -\end_inset +def f1[A, B, C]: ((Either[A, B], C)) => Either[(A, C), (B, C)] = ??? +\end_layout +\begin_layout Plain Layout +def f2[A, B, C]: Either[(A, C), (B, C)] => (Either[A, B], C) = ??? \end_layout \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout - -\size small -\begin_inset Formula $\left(\alpha\wedge\beta\right)\vee\gamma=\left(\alpha\vee\gamma\right)\wedge\left(\beta\vee\gamma\right)$ -\end_inset - - -\end_layout +In the type notation, these type signatures are written as: +\begin_inset Formula +\begin{align*} + & f_{1}^{A,B,C}:\left(A+B\right)\times C\rightarrow A\times C+B\times C\quad,\\ + & f_{2}^{A,B,C}:A\times C+B\times C\rightarrow\left(A+B\right)\times C\quad. +\end{align*} \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +Since the two logical formulas ( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive-1a" +plural "false" +caps "false" +noprefix "false" -\size small -\begin_inset Formula $\left(A\times B\right)+C\not\cong\left(A+C\right)\times\left(B+C\right)$ \end_inset - -\end_layout - -\end_inset - - - +)–( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive-1b" +plural "false" +caps "false" +noprefix "false" \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset Caption Standard +) are true theorems in constructive logic, we expect to be able to implement + the functions +\begin_inset listings +inline true +status open \begin_layout Plain Layout -Logic identities with disjunction and conjunction, and the possible type - equivalences. -\begin_inset CommandInset label -LatexCommand label -name "tab:Logical-identities-with-disjunction-and-conjunction" - -\end_inset - +f1 \end_layout \end_inset + and +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +f2 \end_layout \end_inset - -\end_layout - -\begin_layout Standard -We already verified the first line and the last three lines of Table +. + It is not straightforward to guess how to combine the proof rules of Table \begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "tab:Logical-identities-with-disjunction-and-conjunction" +reference "tab:Proof-rules-of-constructive-and-boolean" plural "false" caps "false" noprefix "false" \end_inset -. - Other identities are verified in a similar way. - Let us begin with lines 3 and 4 of Table + to obtain proofs of Eqs. \begin_inset space ~ \end_inset - +( \begin_inset CommandInset ref LatexCommand ref -reference "tab:Logical-identities-with-disjunction-and-conjunction" +reference "eq:ch-example-distributive-1a" plural "false" caps "false" noprefix "false" \end_inset -, which involve the proposition -\begin_inset Formula $False$ +)–( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive-1b" +plural "false" +caps "false" +noprefix "false" + \end_inset - and the corresponding -\begin_inset Index idx +). + So, instead of deriving the implementations of +\begin_inset listings +inline true status open \begin_layout Plain Layout -void type -\end_layout -\end_inset +f1 +\end_layout -void type -\begin_inset Formula $\bbnum 0$ \end_inset - (Scala's + and \begin_inset listings inline true status open \begin_layout Plain Layout -Nothing +f2 \end_layout \end_inset -). - Reasoning about the void type needs a special technique that we will now - develop while verifying the type isomorphisms -\begin_inset Formula $\bbnum 0\times A\cong\bbnum 0$ -\end_inset - - and -\begin_inset Formula $\bbnum 0+A\cong A$ -\end_inset - -. + from the CH correspondence, we will write the Scala code directly. \end_layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Example-0-times-A" - -\end_inset +\begin_layout Standard +To implement +\begin_inset listings +inline true +status open +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Example-0-times-A" -plural "false" -caps "false" -noprefix "false" +f1 +\end_layout \end_inset - -\begin_inset Index idx +, we need to perform pattern matching on the argument: +\begin_inset listings +inline false status open \begin_layout Plain Layout -solved examples -\end_layout -\end_inset +def f1[A, B, C]: ((Either[A, B], C)) => Either[(A, C), (B, C)] = { +\end_layout +\begin_layout Plain Layout + case (Left(a), c) => Left((a, c)) // No other choice here. \end_layout -\begin_layout Standard -Verify the type equivalence -\begin_inset Formula $\bbnum 0\times A\cong\bbnum 0$ -\end_inset +\begin_layout Plain Layout -. + case (Right(b), c) => Right((b, c)) // No other choice here. \end_layout -\begin_layout Subparagraph -Solution +\begin_layout Plain Layout + +} \end_layout -\begin_layout Standard -Recall that the type notation -\begin_inset Formula $\bbnum 0\times A$ \end_inset - represents the Scala tuple type +In both cases, we have only one possible expression of the correct type. +\end_layout + +\begin_layout Standard +Similarly, the implementation of \begin_inset listings inline true status open \begin_layout Plain Layout -(Nothing, A) +f2 \end_layout \end_inset -. - To demonstrate that the type + leaves us no choices: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -(Nothing, A) +def f2[A, B, C]: Either[(A, C), (B, C)] => (Either[A, B], C) = { \end_layout -\end_inset +\begin_layout Plain Layout - is equivalent to the type -\begin_inset listings -inline true -status open + case Left((a, c)) => (Left(a), c) // No other choice here. +\end_layout \begin_layout Plain Layout -Nothing + case Right((b, c)) => (Right(b), c) // No other choice here. +\end_layout + +\begin_layout Plain Layout + +} \end_layout \end_inset -, we need to show that the type +The code of \begin_inset listings inline true status open \begin_layout Plain Layout -(Nothing, A) +f1 \end_layout \end_inset - has -\emph on -no -\emph default - values. - Indeed, how could we create a value of type, say, + and \begin_inset listings inline true status open \begin_layout Plain Layout -(Nothing, Int) +f2 \end_layout \end_inset -? We would need to fill -\emph on -both -\emph default - parts of the tuple. - We have values of type + never discards any given values; in other words, these functions appear + to preserve information. + We can formulate this property rigorously as a requirement that an arbitrary + value \begin_inset listings inline true status open \begin_layout Plain Layout -Int +x: (Either[A, B], C) \end_layout \end_inset -, but we can never get a value of type + be mapped by \begin_inset listings inline true status open \begin_layout Plain Layout -Nothing +f1 \end_layout \end_inset -. - So, regardless of the type + to some value \begin_inset listings inline true status open \begin_layout Plain Layout -A +y: Either[(A, C), (B, C)] \end_layout \end_inset -, it is impossible to create any values of type + and then mapped by \begin_inset listings inline true status open \begin_layout Plain Layout -(Nothing, A) +f2 \end_layout \end_inset -. - In other words, the set of values of the type + back to +\emph on +the same +\emph default + value \begin_inset listings inline true status open \begin_layout Plain Layout -(Nothing, A) +x \end_layout \end_inset - is empty. - But that is the definition of the void type +. + Similarly, any value \begin_inset listings inline true status open \begin_layout Plain Layout +\noindent -Nothing +y \end_layout \end_inset -. - The types + of type \begin_inset listings inline true status open \begin_layout Plain Layout -(Nothing, A) +Either[(A, C), (B, C)] \end_layout \end_inset - (denoted by -\begin_inset Formula $\bbnum 0\times A$ -\end_inset - -) and + should be transformed by \begin_inset listings inline true status open \begin_layout Plain Layout -Nothing +f2 \end_layout \end_inset - (denoted by -\begin_inset Formula $\bbnum 0$ -\end_inset + and then by +\begin_inset listings +inline true +status open -) are both void and therefore equivalent. -\end_layout +\begin_layout Plain Layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Example-0-plus-A" +f1 +\end_layout \end_inset + back to the same value +\begin_inset listings +inline true +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Example-0-plus-A" -plural "false" -caps "false" -noprefix "false" - -\end_inset - +\begin_layout Plain Layout +y \end_layout -\begin_layout Standard -Verify the type equivalence -\begin_inset Formula $\bbnum 0+A\cong A$ \end_inset . \end_layout -\begin_layout Subparagraph -Solution -\end_layout - \begin_layout Standard -Recall that the type notation -\begin_inset Formula $\bbnum 0+A$ +Let us write these conditions as equations: +\begin_inset Formula +\[ +\forall x^{:(A+B)\times C}.\,f_{2}(f_{1}(x))=x\quad,\quad\quad\forall y^{:A\times C+B\times C}.\,f_{1}\left(f_{2}(y)\right)=y\quad. +\] + \end_inset - represents the Scala type -\begin_inset listings -inline true -status open +If these equations hold, it means that all the information in a value +\begin_inset Formula $x^{:(A+B)\times C}$ +\end_inset -\begin_layout Plain Layout + is completely preserved inside the value +\begin_inset Formula $y\triangleq f_{1}(x)$ +\end_inset -Either[Nothing, A] -\end_layout +; the original value +\begin_inset Formula $x$ +\end_inset + can be recovered as +\begin_inset Formula $x=f_{2}(y)$ \end_inset . - We need to show that any value of that type can be mapped without loss - of information to a value of type -\begin_inset listings -inline true + Then the function +\begin_inset Formula $f_{1}$ +\end_inset + + is the +\series bold +inverse +\series default + +\begin_inset Index idx status open \begin_layout Plain Layout - -A +inverse function \end_layout \end_inset -, and vice versa. - This means implementing functions -\begin_inset Formula $f_{1}:\bbnum 0+A\rightarrow A$ + of +\begin_inset Formula $f_{2}$ \end_inset - and -\begin_inset Formula $f_{2}:A\rightarrow\bbnum 0+A$ +. + Conversely, all the information in a value +\begin_inset Formula $y^{:A\times C+B\times C}$ \end_inset - such that -\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ + is preserved inside +\begin_inset Formula $x\triangleq f_{2}(y)$ \end_inset - and -\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ + and can be recovered by applying +\begin_inset Formula $f_{1}$ \end_inset . -\end_layout + Since the values +\begin_inset Formula $x^{:(A+B)\times C}$ +\end_inset -\begin_layout Standard -The argument of -\begin_inset Formula $f_{1}$ + and +\begin_inset Formula $y^{:A\times C+B\times C}$ \end_inset - is of type -\begin_inset listings -inline true + are arbitrary, it will follow that the +\emph on +data types +\emph default + themselves, +\begin_inset Formula $\left(A+B\right)\times C$ +\end_inset + + and +\begin_inset Formula $A\times C+B\times C$ +\end_inset + +, carry equivalent information. + Such types are called equivalent +\begin_inset Index idx status open \begin_layout Plain Layout - -Either[Nothing, A] +types!equivalent \end_layout \end_inset -. - How can we create a value of that type? Our only choices are to create - a -\begin_inset listings -inline true + or isomorphic +\begin_inset Index idx status open \begin_layout Plain Layout - -Left(x) +types!isomorphic \end_layout \end_inset - with -\begin_inset listings -inline true + +\begin_inset Index idx status open \begin_layout Plain Layout +isomorphic types +\end_layout -x:Nothing +\end_inset + +. \end_layout +\begin_layout Standard +Generally, we say that types +\begin_inset Formula $P$ \end_inset -, or to create a -\begin_inset listings -inline true + and +\begin_inset Formula $Q$ +\end_inset + + are +\series bold +equivalent +\series default + or +\series bold +isomorphic +\series default + (denoted +\begin_inset Formula $P\cong Q$ +\end_inset + +) +\begin_inset Index idx status open \begin_layout Plain Layout - -Right(y) +type equivalence \end_layout \end_inset - with +when there exist functions +\begin_inset Formula $f_{1}:P\rightarrow Q$ +\end_inset + + and +\begin_inset Formula $f_{2}:Q\rightarrow P$ +\end_inset + + that are inverses of each other. + We can write these conditions using the notation +\begin_inset Formula $(f_{1}\bef f_{2})(x)\triangleq f_{2}(f_{1}(x))$ +\end_inset + + as: +\begin_inset Formula +\[ +f_{1}\bef f_{2}=\text{id}\quad,\quad\quad f_{2}\bef f_{1}=\text{id}\quad. +\] + +\end_inset + +(In Scala, the forward composition +\begin_inset Formula $f_{1}\bef f_{2}$ +\end_inset + + is the function \begin_inset listings inline true status open \begin_layout Plain Layout -y:A +f1 andThen f2 \end_layout \end_inset . - However, we cannot create a value -\begin_inset listings -inline true -status open + We omit type annotations since we already checked that the types match.) + If these conditions hold, there is a one-to-one correspondence between + values of types +\begin_inset Formula $P$ +\end_inset -\begin_layout Plain Layout + and +\begin_inset Formula $Q$ +\end_inset -x -\end_layout +. + This is the same as to say that the data types +\begin_inset Formula $P$ +\end_inset + and +\begin_inset Formula $Q$ \end_inset - of type + +\begin_inset Quotes eld +\end_inset + +carry equivalent information +\begin_inset Quotes erd +\end_inset + +. +\end_layout + +\begin_layout Standard +To verify that the Scala functions \begin_inset listings inline true status open \begin_layout Plain Layout -Nothing +f1 \end_layout \end_inset - because the type + and \begin_inset listings inline true status open \begin_layout Plain Layout -Nothing +f2 \end_layout \end_inset - has -\emph on -no -\emph default - values. - We cannot create a -\begin_inset listings -inline true -status open + defined above are inverses of each other, we first check if +\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ +\end_inset -\begin_layout Plain Layout +. + Applying +\begin_inset Formula $f_{1}\bef f_{2}$ +\end_inset -Left(x) -\end_layout + means to apply +\begin_inset Formula $f_{1}$ +\end_inset + + and then to apply +\begin_inset Formula $f_{2}$ +\end_inset + + to the result. + Begin by applying +\begin_inset Formula $f_{1}$ +\end_inset + to an arbitrary value +\begin_inset Formula $x^{:(A+B)\times C}$ \end_inset . - The only remaining possibility is to create a + A value +\begin_inset Formula $x$ +\end_inset + + of that type can be in only one of the two disjoint cases: a tuple \begin_inset listings inline true status open \begin_layout Plain Layout -Right(y) +(Left(a), c) \end_layout \end_inset - with some value + or a tuple \begin_inset listings inline true status open \begin_layout Plain Layout -y +(Right(b), c) \end_layout \end_inset - of type +, for some values \begin_inset listings inline true status open \begin_layout Plain Layout -A +a:A \end_layout \end_inset -. - So, any values of type -\begin_inset Formula $\bbnum 0+A$ -\end_inset - - must be of the form +, \begin_inset listings inline true status open \begin_layout Plain Layout -Right(y) +b:B \end_layout \end_inset -, and we can extract that +, and \begin_inset listings inline true status open \begin_layout Plain Layout -y +c:C \end_layout \end_inset - to obtain a value of type +. + The Scala code of \begin_inset listings inline true status open \begin_layout Plain Layout -A +f1 \end_layout \end_inset -: + maps these tuples to \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f1[A]: Either[Nothing, A] => A = { -\end_layout - -\begin_layout Plain Layout - - case Right(y) => y +Left((a, c)) \end_layout -\begin_layout Plain Layout +\end_inset - // No need for `case Left(x) => ...` since no `x` can ever be given as `Left(x)`. -\end_layout + and to +\begin_inset listings +inline true +status open \begin_layout Plain Layout -} +Right((b, c)) \end_layout \end_inset -For the same reason, there is only one implementation of the function + respectively; we can see this directly from the code of \begin_inset listings inline true status open \begin_layout Plain Layout -f2 +f1 \end_layout \end_inset -: +. + We then apply +\begin_inset Formula $f_{2}$ +\end_inset + + to those values, which maps them back to a tuple \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f2[A]: A => Either[Nothing, A] = { y => Right(y) } +(Left(a), c) \end_layout \end_inset -It is clear from the code that the functions + or to a tuple \begin_inset listings inline true status open \begin_layout Plain Layout -f1 +(Right(b), c) \end_layout \end_inset - and + respectively, according to the code of \begin_inset listings inline true status open @@ -14746,235 +14976,344 @@ f2 \end_inset - are inverses of each other. +. + These tuples are exactly the value +\begin_inset Formula $x$ +\end_inset + + we started with. + So, applying +\begin_inset Formula $f_{1}\bef f_{2}$ +\end_inset + + to an arbitrary +\begin_inset Formula $x^{:(A+B)\times C}$ +\end_inset + + returns that value +\begin_inset Formula $x$ +\end_inset + +. + This is the same as to say that +\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ +\end_inset + +. \end_layout \begin_layout Standard -We have just seen that a value of type -\begin_inset Formula $\bbnum 0+A$ +To check whether +\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ \end_inset - is always a +, we apply +\begin_inset Formula $f_{2}$ +\end_inset + + to an arbitrary value +\begin_inset Formula $y^{:A\times C+B\times C}$ +\end_inset + +, which must be one of the two disjoint cases, \begin_inset listings inline true status open \begin_layout Plain Layout -Right(y) +Left((a, c)) \end_layout \end_inset - with some + or \begin_inset listings inline true status open \begin_layout Plain Layout -y:A +Right((b, c)) \end_layout \end_inset . - Similarly, a value of type -\begin_inset Formula $A+\bbnum 0$ -\end_inset - - is always a + The code of \begin_inset listings inline true status open \begin_layout Plain Layout -Left(x) +f2 \end_layout \end_inset - with some + maps these two cases into tuples \begin_inset listings inline true status open \begin_layout Plain Layout -x:A +(Left(a), c) \end_layout -\end_inset - -. - So, we will use the notation -\begin_inset Formula $A+\bbnum 0$ \end_inset and -\begin_inset Formula $\bbnum 0+A$ -\end_inset - - to -\emph on -denote -\emph default - the \begin_inset listings inline true status open \begin_layout Plain Layout -Left +(Right(b), c) \end_layout \end_inset - and the + respectively. + Then we apply \begin_inset listings inline true status open \begin_layout Plain Layout -Right +f1 \end_layout \end_inset - parts of the disjunctive type + and map these tuples back to \begin_inset listings inline true status open \begin_layout Plain Layout -Either +Left((a, c)) \end_layout \end_inset -. - This notation agrees with the behavior of the Scala compiler, which will - infer the types + and \begin_inset listings inline true status open \begin_layout Plain Layout -Either[A, Nothing] +Right((b, c)) \end_layout \end_inset -or + respectively. + It follows that applying +\begin_inset Formula $f_{2}$ +\end_inset + + and then +\begin_inset Formula $f_{1}$ +\end_inset + + will always return the initial value. + As a formula, this is written as +\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ +\end_inset + +. +\end_layout + +\begin_layout Standard +By looking at the code of \begin_inset listings inline true status open \begin_layout Plain Layout -Either[Nothing, A] +f1 \end_layout \end_inset - for these parts: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "53col%" + and +\begin_inset listings +inline true status open \begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset +f2 +\end_layout + +\end_inset +, we can directly observe that these functions are inverses of each other: + the tuple pattern \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def toLeft[A, B]: A => Either[A, B] = x => Left(x) +(Left(a), c) \end_layout -\begin_layout Plain Layout +\end_inset -def toRight[A, B]: B => Either[A, B] = y => Right(y) -\end_layout + is mapped to +\begin_inset listings +inline true +status open \begin_layout Plain Layout +Left((a, c)) \end_layout -\begin_layout Plain Layout +\end_inset -scala> toLeft(123) -\end_layout +, and the pattern +\begin_inset listings +inline true +status open \begin_layout Plain Layout -res0: Either[Int, Nothing] = Left(123) +(Right(b), c) \end_layout +\end_inset + + to +\begin_inset listings +inline true +status open + \begin_layout Plain Layout +Right((b, c)) \end_layout -\begin_layout Plain Layout +\end_inset -scala> toRight( -\begin_inset Quotes eld +, or vice versa. + It is visually clear that no information is lost and that the original + values are returned by function compositions +\begin_inset Formula $f_{1}\bef f_{2}$ \end_inset -abc -\begin_inset Quotes erd + or +\begin_inset Formula $f_{2}\bef f_{1}$ \end_inset -) +. \end_layout -\begin_layout Plain Layout +\begin_layout Standard +We find that the logical identity +\begin_inset space ~ +\end_inset -res1: Either[Nothing, String] = Right("abc") -\end_layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive-1" +plural "false" +caps "false" +noprefix "false" \end_inset +) leads to an equivalence of the corresponding types: +\begin_inset Formula +\begin{equation} +\left(A+B\right)\times C\cong A\times C+B\times C\quad.\label{eq:ch-distributive-law-types} +\end{equation} + +\end_inset -\end_layout +To get Eq. +\begin_inset space ~ +\end_inset + +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-distributive-law-types" +plural "false" +caps "false" +noprefix "false" -\begin_layout Plain Layout -\begin_inset VSpace -200baselineskip% \end_inset +) from Eq. +\begin_inset space ~ +\end_inset -\end_layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +), we need to convert a logical formula to an arithmetic expression by replacing + the disjunction operations +\begin_inset Formula $\vee$ +\end_inset + + by +\begin_inset Formula $+$ +\end_inset + and the conjunctions +\begin_inset Formula $\wedge$ \end_inset + by +\begin_inset Formula $\times$ +\end_inset + everywhere. \end_layout \begin_layout Standard -\noindent -We can write the functions +As another example of a logical identity, consider the associativity law + for conjunction: +\begin_inset Formula +\begin{equation} +\left(\alpha\wedge\beta\right)\wedge\gamma=\alpha\wedge\left(\beta\wedge\gamma\right)\quad.\label{eq:ch-example-associativity-conjunction} +\end{equation} + +\end_inset + +The corresponding types are +\begin_inset Formula $(A\times B)\times C$ +\end_inset + + and +\begin_inset Formula $A\times(B\times C)$ +\end_inset + +; in Scala, \begin_inset listings inline true status open \begin_layout Plain Layout -toLeft +((A, B), C) \end_layout \end_inset @@ -14986,310 +15325,252 @@ status open \begin_layout Plain Layout -toRight +(A, (B, C)) \end_layout \end_inset - in a code notation as: -\begin_inset Formula -\begin{align*} - & \text{toLeft}^{A,B}\triangleq x^{:A}\rightarrow x+\bbnum 0^{:B}\quad,\\ - & \text{toRight}^{A,B}\triangleq y^{:B}\rightarrow\bbnum 0^{:A}+y\quad. -\end{align*} +. + We can define functions that convert between these types without information + loss +\begin_inset Index idx +status open + +\begin_layout Plain Layout +information loss +\end_layout \end_inset -In this notation, a value of the disjunctive type is shown without using - Scala class names such as +: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -Either +def f3[A, B, C]: (((A, B), C)) => (A, (B, C)) = { case ((a, b), c) => +\end_layout + +\begin_layout Plain Layout + + (a, (b, c)) } +\end_layout + +\begin_layout Plain Layout + +def f4[A, B, C]: (A, (B, C)) => (((A, B), C)) = { case (a, (b, c)) => +\end_layout + +\begin_layout Plain Layout + + ((a, b), c) } \end_layout \end_inset -, +By applying these functions to arbitrary values of types \begin_inset listings inline true status open \begin_layout Plain Layout -Right +((A, B), C) \end_layout \end_inset -, and + and \begin_inset listings inline true status open \begin_layout Plain Layout -Left +(A, (B, C)) \end_layout \end_inset -. - This shortens the writing and speeds up code reasoning. - -\end_layout +, it is easy to see that the functions +\begin_inset listings +inline true +status open -\begin_layout Standard -The type annotation -\begin_inset Formula $\bbnum 0^{:A}$ -\end_inset +\begin_layout Plain Layout - is helpful to remind ourselves about the type parameter -\begin_inset Formula $A$ -\end_inset +f3 +\end_layout - used, e.g., by the disjunctive value -\begin_inset Formula $\bbnum 0^{:A}+y^{:B}$ \end_inset - in the body of + and \begin_inset listings inline true status open \begin_layout Plain Layout -toRight[A, B] +f4 \end_layout \end_inset -. - Without this type annotation, -\begin_inset Formula $\bbnum 0+y^{:B}$ -\end_inset - - means a value of type + are inverses of each other. + This is also directly visible in the code: the nested tuple pattern \begin_inset listings inline true status open \begin_layout Plain Layout -Either[A, B] +((a, b), c) \end_layout \end_inset - where the parameter -\begin_inset Formula $A$ -\end_inset - - should be determined by matching the types of other expressions. - When it is clear what types are being used, we may omit type annotations - and write simply -\begin_inset Formula $\bbnum 0+y$ -\end_inset + is mapped to the pattern +\begin_inset listings +inline true +status open - instead of -\begin_inset Formula $\bbnum 0^{:A}+y^{:B}$ -\end_inset +\begin_layout Plain Layout -. +(a, (b, c)) \end_layout -\begin_layout Standard -In the notation -\begin_inset Formula $\bbnum 0+y^{:B}$ -\end_inset - -, we use the symbol -\begin_inset Formula $\bbnum 0$ \end_inset - rather than an ordinary zero ( -\begin_inset Formula $0$ + and back. + So, the types +\begin_inset Formula $\left(A\times B\right)\times C$ \end_inset -), to avoid suggesting that -\begin_inset Formula $0$ + and +\begin_inset Formula $A\times\left(B\times C\right)$ \end_inset - is a value of type -\begin_inset Formula $\bbnum 0$ + are equivalent, and we can write +\begin_inset Formula $A\times B\times C$ \end_inset -. - The void type -\begin_inset Formula $\bbnum 0$ -\end_inset + without parentheses. +\end_layout - has +\begin_layout Standard +Does a logical identity always correspond to an equivalence of types? This + turns out to be \emph on -no +not \emph default - values, unlike the -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout + so. + A simple example of a logical identity that does not correspond to a type + equivalence is: +\begin_inset Formula +\begin{equation} +True\vee\alpha=True\quad.\label{eq:ch-example-logic-identity-2} +\end{equation} -Unit -\end_layout +\end_inset +Since the CH correspondence maps the logical constant +\begin_inset Formula $True$ \end_inset - type, + into the unit type \begin_inset Formula $\bbnum 1$ \end_inset -, which has a value denoted by -\begin_inset Formula $1$ +, the type equivalence corresponding to Eq. +\begin_inset space ~ \end_inset - in the code notation. -\end_layout - -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Example-1xA" - -\end_inset - - +( \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-Example-1xA" +reference "eq:ch-example-logic-identity-2" plural "false" caps "false" noprefix "false" \end_inset - -\end_layout - -\begin_layout Standard -Verify the type equivalence -\begin_inset Formula $A\times\bbnum 1\cong A$ +) is +\begin_inset Formula $\bbnum 1+A\cong\bbnum 1$ \end_inset . -\end_layout - -\begin_layout Subparagraph -Solution -\end_layout + The type denoted by +\begin_inset Formula $\bbnum 1+A$ +\end_inset -\begin_layout Standard -The corresponding Scala types are the tuple + means \begin_inset listings inline true status open \begin_layout Plain Layout -(A, Unit) +Option[A] \end_layout \end_inset - and the type + in Scala, so the corresponding equivalence is \begin_inset listings inline true status open \begin_layout Plain Layout -A +Option[A] \end_layout \end_inset -. - We need to implement functions -\begin_inset Formula $f_{1}:\forall A.\,A\times\bbnum 1\rightarrow A$ -\end_inset - and -\begin_inset Formula $f_{2}:\forall A.\,A\rightarrow A\times\bbnum 1$ +\begin_inset Formula $\cong$ \end_inset - and to demonstrate that they are inverses of each other. - The Scala code for these functions is: + \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f1[A]: ((A, Unit)) => A = { case (a, ()) => a } -\end_layout - -\begin_layout Plain Layout - -def f2[A]: A => (A, Unit) = { a => (a, ()) } +Unit \end_layout \end_inset -Let us first write a proof by reasoning directly with Scala code: +. + Intuitively, this type equivalence should not hold: an \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -(f1 andThen f2)((a,())) == f2(f1((a,())) == f2(a) == (a, ()) -\end_layout - -\begin_layout Plain Layout - -(f2 andThen f1)(a) == f1(f2(a)) == f1((a, ())) = a +Option[A] \end_layout \end_inset -Now let us write a proof in the code notation. - The codes of -\begin_inset Formula $f_{1}$ -\end_inset - - and -\begin_inset Formula $f_{2}$ -\end_inset - - are: -\begin_inset Formula -\[ -f_{1}\triangleq a^{:A}\times1\rightarrow a\quad,\quad\quad f_{2}\triangleq a^{:A}\rightarrow a\times1\quad, -\] - -\end_inset - -where we denoted by -\begin_inset Formula $1$ -\end_inset - - the value + may carry a value of type \begin_inset listings inline true status open \begin_layout Plain Layout -() +A \end_layout \end_inset - of the +, which cannot possibly be stored in a value of type \begin_inset listings inline true status open @@ -15301,460 +15582,322 @@ Unit \end_inset - type. - We find: -\begin_inset Formula -\begin{align*} -(f_{1}\bef f_{2})(a^{:A}\times1) & =f_{2}\left(f_{1}(a\times1)\right)=f_{2}\left(a\right)=a\times1\quad,\\ -(f_{2}\bef f_{1})(a^{:A}) & =f_{1}(f_{2}(a))=f_{1}(a\times1)=a\quad. -\end{align*} - +. + We can verify this intuition rigorously by proving that any fully parametric + functions with type signatures +\begin_inset Formula $g_{1}:\bbnum 1+A\rightarrow\bbnum 1$ \end_inset -This shows that both compositions are identity functions. - Another way of writing the proof is by computing the function compositions - symbolically, without applying to a value -\begin_inset Formula $a^{:A}$ + and +\begin_inset Formula $g_{2}:\bbnum 1\rightarrow\bbnum 1+A$ \end_inset -: -\begin_inset Formula -\begin{align*} -f_{1}\bef f_{2} & =\left(a\times1\rightarrow a\right)\bef\left(a\rightarrow a\times1\right)=\left(a\times1\rightarrow a\times1\right)=\text{id}^{A\times\bbnum 1}\quad,\\ -f_{2}\bef f_{1} & =\left(a\rightarrow a\times1\right)\bef\left(a\times1\rightarrow a\right)=\left(a\rightarrow a\right)=\text{id}^{A}\quad. -\end{align*} - + will not satisfy +\begin_inset Formula $g_{1}\bef g_{2}=\text{id}$ \end_inset - -\end_layout - -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Example-A+B" - +. + To verify this, we note that +\begin_inset Formula $g_{2}:\bbnum 1\rightarrow\bbnum 1+A$ \end_inset + must have this type signature: +\begin_inset listings +inline false +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Example-A+B" -plural "false" -caps "false" -noprefix "false" - -\end_inset - +\begin_layout Plain Layout +def g2[A]: Unit => Option[A] = ??? \end_layout -\begin_layout Standard -Verify the type equivalence -\begin_inset Formula $A+B\cong B+A$ \end_inset -. -\end_layout - -\begin_layout Subparagraph -Solution -\end_layout - -\begin_layout Standard -The corresponding Scala types are +This function must always return \begin_inset listings inline true status open \begin_layout Plain Layout -Either[A, B] +None \end_layout \end_inset - and +, since a fully parametric function cannot produce values of an arbitrary + type \begin_inset listings inline true status open \begin_layout Plain Layout -Either[B, A] +A \end_layout \end_inset -. - We use pattern matching to implement the functions required for the type - equivalence: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "55col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -86baselineskip% + from scratch. + Therefore, +\begin_inset Formula $g_{1}\bef g_{2}$ \end_inset - + is also a function that always returns \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f1[A, B]: Either[A, B] => Either[B, A] = { -\end_layout - -\begin_layout Plain Layout - - case Left(a) => Right(a) // No other choice here. -\end_layout - -\begin_layout Plain Layout - - case Right(b) => Left(b) // No other choice here. -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\begin_layout Plain Layout - -def f2[A, B]: Either[B, A] => Either[A, B] = f1[B, A] +None \end_layout \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -120baselineskip% +. + The function +\begin_inset Formula $g_{1}\bef g_{2}$ \end_inset - -\end_layout - + has type signature +\begin_inset Formula $\bbnum 1+A\rightarrow\bbnum 1+A$ \end_inset -The functions + or, in Scala syntax, \begin_inset listings inline true status open \begin_layout Plain Layout -f1 +Option[A] => Option[A] \end_layout \end_inset - and +, and is not equal to the identity function, because the identity function + does +\emph on +not +\emph default + always return \begin_inset listings inline true status open \begin_layout Plain Layout -f2 +None \end_layout \end_inset - are implemented by code that can be derived unambiguously from the type - signatures. - For instance, the line -\begin_inset listings -inline true -status open +. +\end_layout -\begin_layout Plain Layout +\begin_layout Standard +Another example of a logical identity without a type equivalence is the + distributive law: +\begin_inset Formula +\begin{equation} +\forall(A,B,C).\,\left(A\wedge B\right)\vee C=\left(A\vee C\right)\wedge\left(B\vee C\right)\quad,\label{eq:ch-example-distributive-2} +\end{equation} -case Left(a) => ... -\end_layout +\end_inset +which is +\begin_inset Quotes eld \end_inset - is required to return a value of type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Either[B, A] -\end_layout - +dual +\begin_inset Quotes erd \end_inset - by using a given value -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -a:A -\end_layout - + to the law +\begin_inset space ~ \end_inset -. - The only way of doing that is by returning -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Right(a) -\end_layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive-1" +plural "false" +caps "false" +noprefix "false" \end_inset -. -\end_layout - -\begin_layout Standard -It is clear from the code that the functions -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -f1 -\end_layout - +), i.e., it is obtained from Eq. +\begin_inset space ~ \end_inset - and -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -f2 -\end_layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive-1" +plural "false" +caps "false" +noprefix "false" \end_inset - are inverses of each other. - To verify that rigorously, we need to show that -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -f1 andThen f2 -\end_layout - +) by swapping all conjunctions ( +\begin_inset Formula $\wedge$ \end_inset - is equal to an identity function. - The function -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -f1 andThen f2 -\end_layout - +) with disjunctions ( +\begin_inset Formula $\vee$ \end_inset - applies -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -f2 -\end_layout - +). + In logic, a dual formula to an identity is also an identity. + The CH correspondence maps Eq. +\begin_inset space ~ \end_inset - to the result of -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -f1 -\end_layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive-2" +plural "false" +caps "false" +noprefix "false" \end_inset -. - The code of -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -f1 -\end_layout +) into the type equation: +\begin_inset Formula +\begin{equation} +\forall(A,B,C).\,\left(A\times B\right)+C=\left(A+C\right)\times\left(B+C\right)\quad.\label{eq:ch-example-incorrect-identity-2} +\end{equation} \end_inset - contains two -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -case ... -\end_layout - +However, the types +\begin_inset Formula $A\times B+C$ \end_inset - lines, each returning a result. - So, we need to apply -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -f2 -\end_layout + and +\begin_inset Formula $\left(A+C\right)\times\left(B+C\right)$ +\end_inset + are +\emph on +not +\emph default + equivalent. + To see why, look at the possible code of a function +\begin_inset Formula $g_{3}:\left(A+C\right)\times\left(B+C\right)\rightarrow A\times B+C$ \end_inset - separately in each line. - Evaluate the code symbolically: +: \begin_inset listings +lstparams "numbers=left" inline false status open \begin_layout Plain Layout -(f1 andThen f2) == { -\end_layout - -\begin_layout Plain Layout - - case Left(a) => f2(Right(a)) +def g3[A,B,C]: ((Either[A, C], Either[B, C])) => Either[(A, B), C] = { \end_layout \begin_layout Plain Layout - case Right(b) => f2(Left(b)) + case (Left(a), Left(b)) => Left((a, b)) // No other choice. \end_layout \begin_layout Plain Layout -} == { + case (Left(a), Right(c)) => Right(c) // No other choice. \end_layout \begin_layout Plain Layout - case Left(a) => Left(a) + case (Right(c), Left(b)) => Right(c) // No other choice. \end_layout \begin_layout Plain Layout - case Right(b) => Right(b) + case (Right(c1), Right(c2)) => Right(c1) // Must discard c1 or c2 + here! \end_layout \begin_layout Plain Layout -} +} // May return Right(c2) instead of Right(c1) in the last line. \end_layout \end_inset -The result is a function of type +In line 5, we have a choice of returning \begin_inset listings inline true status open \begin_layout Plain Layout -Either[A, B] => Either[A, B] +Right(c1) \end_layout \end_inset - that does not change its argument; so, it is equal to the identity function. - -\end_layout - -\begin_layout Standard -Let us now write the function + or \begin_inset listings inline true status open \begin_layout Plain Layout -f1 +Right(c2) \end_layout \end_inset - in the code notation and perform the same derivation. - We will also develop a useful notation for functions operating on disjunctive - types. -\end_layout - -\begin_layout Standard -The pattern matching construction in the Scala code of -\begin_inset listings -inline true +. + Whichever we choose, we will lose information +\begin_inset Index idx status open \begin_layout Plain Layout - -f1 +information loss \end_layout \end_inset - is similar to a pair of functions with types + because we will have discarded one of the given values \begin_inset listings inline true status open \begin_layout Plain Layout -A => Either[B, A] +c1 \end_layout \end_inset - and +, \begin_inset listings inline true status open \begin_layout Plain Layout -B => Either[B, A] +c2 \end_layout \end_inset . - One of these functions is applied depending on whether the argument of + After evaluating +\begin_inset Formula $g_{3}$ +\end_inset + +, we will not be able to restore +\emph on +both +\emph default \begin_inset listings inline true @@ -15762,1398 +15905,1336 @@ status open \begin_layout Plain Layout -f1 +c1 \end_layout \end_inset - has type -\begin_inset Formula $A+\bbnum 0$ -\end_inset - - or -\begin_inset Formula $\bbnum 0+B$ -\end_inset - -. - So, we may write the code of + and \begin_inset listings inline true status open \begin_layout Plain Layout -f1 +c2 \end_layout \end_inset - as: -\begin_inset Formula -\[ -f_{1}\triangleq x^{:A+B}\rightarrow\begin{cases} -\text{if }x=a^{:A}+\bbnum 0^{:B}\quad: & \bbnum 0^{:B}+a^{:A}\\ -\text{if }x=\bbnum 0^{:A}+b^{:B}\quad: & b^{:B}+\bbnum 0^{:A} -\end{cases} -\] - +, no matter what code we write. + So, the composition +\begin_inset Formula $g_{3}\bef g_{4}$ \end_inset -Since both the argument and the result of -\begin_inset Formula $f_{1}$ + with any +\begin_inset Formula $g_{4}$ \end_inset - are disjunctive types with -\begin_inset Formula $2$ + cannot be equal to the identity function. + The type equation +\begin_inset space ~ \end_inset - parts each, it is convenient to write the code of -\begin_inset Formula $f_{1}$ +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-incorrect-identity-2" +plural "false" +caps "false" +noprefix "false" + \end_inset - as a -\begin_inset Formula $2\times2$ +) is incorrect. +\end_layout + +\begin_layout Standard +We find that a logical identity +\begin_inset Formula ${\cal CH}(P)={\cal CH}(Q)$ \end_inset - + guarantees, via the CH correspondence, that we can implement \emph on -matrix +some \emph default - that maps the input parts to the output parts: -\begin_inset Index idx -status open + fully parametric functions of types +\begin_inset Formula $P\rightarrow Q$ +\end_inset -\begin_layout Plain Layout -disjunctive type!in matrix notation -\end_layout + and +\begin_inset Formula $Q\rightarrow P$ +\end_inset +. + However, it is not guaranteed that these functions are inverses of each + other, i.e., that the type conversions +\begin_inset Formula $P\rightarrow Q$ \end_inset + or +\begin_inset Formula $Q\rightarrow P$ +\end_inset + have no information loss \begin_inset Index idx status open \begin_layout Plain Layout -matrix notation +information loss \end_layout \end_inset - -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% +. + So, the type equivalence +\begin_inset Formula $P\cong Q$ \end_inset + does not automatically follow from the logical identity +\begin_inset Formula ${\cal CH}(P)={\cal CH}(Q)$ +\end_inset -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -def f1[A, B]: Either[A, B] => Either[B, A] = { +. \end_layout -\begin_layout Plain Layout +\begin_layout Standard +The CH correspondence means that for true propositions +\begin_inset Formula ${\cal CH}(X)$ +\end_inset - case Left(a) => Right(a) -\end_layout + we can compute +\emph on +some +\emph default + value +\begin_inset Formula $x$ +\end_inset -\begin_layout Plain Layout + of type +\begin_inset Formula $X$ +\end_inset - case Right(b) => Left(b) -\end_layout +. + However, the CH correspondence does not guarantee that the computed value + +\begin_inset Formula $x^{:X}$ +\end_inset -\begin_layout Plain Layout + will satisfy any additional properties or laws. +\end_layout -} +\begin_layout Subsection +Arithmetic identity corresponds to type equivalence \end_layout +\begin_layout Standard +Looking at the examples of equivalent types, we notice that correct type + equivalences correspond to +\emph on +arithmetical +\emph default + identities rather than +\emph on +logical +\emph default + identities. + For instance, the logical identity in Eq. +\begin_inset space ~ \end_inset +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive-1" +plural "false" +caps "false" +noprefix "false" -\begin_inset VSpace -200baselineskip% \end_inset - -\end_layout - +) leads to the type equivalence +\begin_inset space ~ \end_inset +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-distributive-law-types" +plural "false" +caps "false" +noprefix "false" -\begin_inset VSpace -60baselineskip% \end_inset - +), which looks like a standard identity of arithmetic, such as: \begin_inset Formula \[ -f_{1}\triangleq\,\begin{array}{|c||cc|} - & B & A\\ -\hline A & \bbnum 0 & a^{:A}\rightarrow a\\ -B & b^{:B}\rightarrow b & \bbnum 0 -\end{array}\quad. +(1+10)\times20=1\times20+10\times20\quad. \] \end_inset - -\begin_inset VSpace -50baselineskip% +The logical identity in Eq. +\begin_inset space ~ \end_inset - -\end_layout - -\begin_layout Standard -The rows of the matrix correspond to the -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -case -\end_layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-distributive-2" +plural "false" +caps "false" +noprefix "false" \end_inset - rows in the Scala code. - There is one row for each part of the disjunctive type of the argument. - The columns of the matrix correspond to the parts of the disjunctive type - of the result. -\begin_inset Index idx -status open +), which does +\emph on +not +\emph default + yield a type equivalence, leads to an incorrect arithmetic equation +\begin_inset space ~ +\end_inset -\begin_layout Plain Layout -pattern matching!in matrix notation -\end_layout -\end_inset +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-incorrect-identity-2" +plural "false" +caps "false" +noprefix "false" - The matrix element in row -\begin_inset Formula $A$ \end_inset - and column -\begin_inset Formula $A$ +, e.g., +\begin_inset Formula $\left(1\times10\right)+20\neq\left(1+20\right)\times\left(10+20\right)$ \end_inset - is a function of type -\begin_inset Formula $A\rightarrow A$ +. + Similarly, the associativity law +\begin_inset space ~ \end_inset - that corresponds to the line -\begin_inset listings -inline true -status open +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-associativity-conjunction" +plural "false" +caps "false" +noprefix "false" -\begin_layout Plain Layout +\end_inset -case Left(a) => Right(a) -\end_layout +) leads to a type equivalence and to the arithmetic identity: +\begin_inset Formula +\[ +\left(a\times b\right)\times c=a\times\left(b\times c\right)\quad, +\] \end_inset - in the Scala code. - The matrix element in row -\begin_inset Formula $A$ +The logical identity in Eq. +\begin_inset space ~ \end_inset - and column -\begin_inset Formula $B$ +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-logic-identity-2" +plural "false" +caps "false" +noprefix "false" + \end_inset - is written as -\begin_inset Formula $\bbnum 0$ +), which does not yield a type equivalence, leads to an incorrect arithmetic + statement ( +\begin_inset Formula $\forall a.\,1+a=1$ \end_inset - because no value of that type is returned. - In this way, we translate each line of the -\begin_inset listings -inline true -status open +). +\end_layout -\begin_layout Plain Layout +\begin_layout Standard +Table +\begin_inset space ~ +\end_inset -match / case -\end_layout + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Logical-identities-with-disjunction-and-conjunction" +plural "false" +caps "false" +noprefix "false" \end_inset - expression into a code matrix. + summarizes these and other examples of logical identities and the corresponding + type equivalences. + In all rows, quantifiers such as +\begin_inset Formula $\forall\alpha$ +\end_inset + + or +\begin_inset Formula $\forall(A,B)$ +\end_inset + + are implied as necessary. \end_layout \begin_layout Standard -The code of -\begin_inset Formula $f_{2}$ +Because we chose the type notation to be similar to the ordinary arithmetic + notation, it is easy to translate a possible type equivalence into an arithmeti +c equation. + In all cases, valid arithmetic identities correspond to type equivalences, + and failures to obtain a type equivalence correspond to incorrect arithmetic + identities. + With regard to type equivalence, types such as +\begin_inset Formula $A+B$ \end_inset - is written similarly. - Let us rename arguments for clarity: -\begin_inset space \hfill{} + and +\begin_inset Formula $A\times B$ \end_inset - -\begin_inset space ~ + behave similarly to arithmetic expressions such as +\begin_inset Formula $10+20$ \end_inset + and +\begin_inset Formula $10\times20$ +\end_inset -\begin_inset Wrap figure -lines 4 -placement l -overhang 0in -width "50col%" -status open + and not similarly to logical formulas such as +\begin_inset Formula $\alpha\vee\beta$ +\end_inset -\begin_layout Plain Layout -\begin_inset VSpace -70baselineskip% + and +\begin_inset Formula $\alpha\wedge\beta$ \end_inset +. +\end_layout -\begin_inset listings -inline false +\begin_layout Standard +\begin_inset Float table +wide false +sideways false status open \begin_layout Plain Layout - -def f2[A, B]: Either[B, A] => Either[A, B] = { -\end_layout +\align center +\begin_inset Tabular + + + + + + +\begin_inset Text \begin_layout Plain Layout - case Left(y) => Right(y) +\series bold +\size small +Logical identity \end_layout -\begin_layout Plain Layout - - case Right(x) => Left(x) -\end_layout +\end_inset + + +\begin_inset Text \begin_layout Plain Layout -} +\series bold +\size small +Type equivalence (if it holds) \end_layout \end_inset + + + + +\begin_inset Text +\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% +\size small +\begin_inset Formula $True\vee\alpha=True$ \end_inset \end_layout \end_inset + + +\begin_inset Text +\begin_layout Plain Layout -\end_layout - -\begin_layout Standard -\begin_inset VSpace -150baselineskip% +\size small +\begin_inset Formula $\bbnum 1+A\not\cong\bbnum 1$ \end_inset -\begin_inset Formula -\[ -f_{2}\triangleq\,\begin{array}{|c||cc|} - & A & B\\ -\hline B & \bbnum 0 & y^{:B}\rightarrow y\\ -A & x^{:A}\rightarrow x & \bbnum 0 -\end{array}\quad. -\] +\end_layout \end_inset + + + + +\begin_inset Text +\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% +\size small +\begin_inset Formula $True\wedge\alpha=\alpha$ \end_inset \end_layout -\begin_layout Standard -\noindent -The forward composition -\begin_inset Formula $f_{1}\bef f_{2}$ \end_inset - - is computed by the standard rules of row-by-column matrix multiplication. -\begin_inset Foot -status open + + +\begin_inset Text \begin_layout Plain Layout -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://en.wikipedia.org/wiki/Matrix_multiplication" - +\size small +\begin_inset Formula $\bbnum 1\times A\cong A$ \end_inset \end_layout \end_inset + + + + +\begin_inset Text - Any terms containing -\begin_inset Formula $\bbnum 0$ -\end_inset - - are omitted, and the remaining functions are composed: -\begin_inset Formula -\begin{align*} -f_{1}\bef f_{2} & =\,\begin{array}{|c||cc|} - & B & A\\ -\hline A & \bbnum 0 & a^{:A}\rightarrow a\\ -B & b^{:B}\rightarrow b & \bbnum 0 -\end{array}\,\bef\,\begin{array}{|c||cc|} - & A & B\\ -\hline B & \bbnum 0 & y^{:B}\rightarrow y\\ -A & x^{:A}\rightarrow x & \bbnum 0 -\end{array}\\ -\text{matrix multiplication/composition}:\quad & =\,\,\begin{array}{|c||cc|} - & A & B\\ -\hline A & (a^{:A}\rightarrow a)\bef(x^{:A}\rightarrow x) & \bbnum 0\\ -B & \bbnum 0 & (b^{:B}\rightarrow b)\bef(y^{:B}\rightarrow y) -\end{array}\\ -\text{function composition}:\quad & =\,\begin{array}{|c||cc|} - & A & B\\ -\hline A & \text{id} & \bbnum 0\\ -B & \bbnum 0 & \text{id} -\end{array}\,=\text{id}^{:A+B\rightarrow A+B}\quad. -\end{align*} +\begin_layout Plain Layout +\size small +\begin_inset Formula $False\vee\alpha=\alpha$ \end_inset \end_layout -\begin_layout Standard -Several features of the matrix notation are helpful in such calculations. - The parts of the code of -\begin_inset Formula $f_{1}$ \end_inset + + +\begin_inset Text - are automatically composed with the corresponding parts of the code of - -\begin_inset Formula $f_{2}$ -\end_inset +\begin_layout Plain Layout -. - To check that the types match in the function composition, we just need - to compare the types in the output row -\begin_inset Formula $\,\begin{array}{||cc|} -B & A\end{array}\,$ +\size small +\begin_inset Formula $\bbnum 0+A\cong A$ \end_inset - of -\begin_inset Formula $f_{1}$ -\end_inset - with the input column -\begin_inset Formula $\,\begin{array}{|c||} -B\\ -A -\end{array}\,$ -\end_inset +\end_layout - of -\begin_inset Formula $f_{2}$ \end_inset + + + + +\begin_inset Text -. - Once we verified that all types match, we may omit the type annotations - and write the same derivation more concisely as: -\begin_inset Formula -\begin{align*} -f_{1}\bef f_{2} & =\,\begin{array}{||cc|} -\bbnum 0 & a^{:A}\rightarrow a\\ -b^{:B}\rightarrow b & \bbnum 0 -\end{array}\,\bef\,\begin{array}{||cc|} -\bbnum 0 & y^{:B}\rightarrow y\\ -x^{:A}\rightarrow x & \bbnum 0 -\end{array}\\ -\text{matrix multiplication/composition}:\quad & =\,\,\begin{array}{||cc|} -(a^{:A}\rightarrow a)\bef(x^{:A}\rightarrow x) & \bbnum 0\\ -\bbnum 0 & (b^{:B}\rightarrow b)\bef(y^{:B}\rightarrow y) -\end{array}\\ -\text{function composition}:\quad & =\,\begin{array}{||cc|} -\text{id} & \bbnum 0\\ -\bbnum 0 & \text{id} -\end{array}\,=\text{id}\quad. -\end{align*} +\begin_layout Plain Layout +\size small +\begin_inset Formula $False\wedge\alpha=False$ \end_inset -The identity function is represented by the diagonal matrix: -\begin_inset Formula $\,\begin{array}{||cc|} -\text{id} & \bbnum 0\\ -\bbnum 0 & \text{id} -\end{array}$ -\end_inset +\end_layout -\begin_inset space ~ \end_inset + + +\begin_inset Text -. -\end_layout - -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-AxB" +\begin_layout Plain Layout +\size small +\begin_inset Formula $\bbnum 0\times A\cong\bbnum 0$ \end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-AxB" -plural "false" -caps "false" -noprefix "false" +\end_layout \end_inset - - -\begin_inset Index idx -status open + + + + +\begin_inset Text \begin_layout Plain Layout -exercises -\end_layout +\size small +\begin_inset Formula $\alpha\vee\beta=\beta\vee\alpha$ \end_inset \end_layout -\begin_layout Standard -Verify the type equivalence -\begin_inset Formula $A\times B\cong B\times A$ \end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\size small +\begin_inset Formula $A+B\cong B+A$ +\end_inset + -. \end_layout -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-A+B+C" +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout +\size small +\begin_inset Formula $\alpha\wedge\beta=\beta\wedge\alpha$ \end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-A+B+C" -plural "false" -caps "false" -noprefix "false" +\end_layout \end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\size small +\begin_inset Formula $A\times B\cong B\times A$ +\end_inset \end_layout -\begin_layout Standard -Verify the type equivalence -\begin_inset Formula $\left(A+B\right)+C\cong A+\left(B+C\right)$ \end_inset + + + + +\begin_inset Text -. - Since Section -\begin_inset space ~ +\begin_layout Plain Layout + +\size small +\begin_inset Formula $\left(\alpha\vee\beta\right)\vee\gamma=\alpha\vee\left(\beta\vee\gamma\right)$ \end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Logical-identity-not-type-equivalence" -plural "false" -caps "false" -noprefix "false" +\end_layout \end_inset + + +\begin_inset Text - proved the equivalences +\begin_layout Plain Layout + +\size small \begin_inset Formula $\left(A+B\right)+C\cong A+\left(B+C\right)$ \end_inset - and -\begin_inset Formula $\left(A\times B\right)\times C\cong A\times\left(B\times C\right)$ -\end_inset -, we may write -\begin_inset Formula $A+B+C$ +\end_layout + \end_inset + + + + +\begin_inset Text - and -\begin_inset Formula $A\times B\times C$ +\begin_layout Plain Layout + +\size small +\begin_inset Formula $\left(\alpha\wedge\beta\right)\wedge\gamma=\alpha\wedge\left(\beta\wedge\gamma\right)$ \end_inset - without any parentheses. -\end_layout -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-A+B2" +\end_layout \end_inset + + +\begin_inset Text +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-A+B2" -plural "false" -caps "false" -noprefix "false" - +\size small +\begin_inset Formula $\left(A\times B\right)\times C\cong A\times\left(B\times C\right)$ \end_inset \end_layout -\begin_layout Standard -Verify the type equivalence: -\begin_inset Formula -\[ -\left(A+B\right)\times\left(A+B\right)=A\times A+\bbnum 2\times A\times B+B\times B\quad, -\] - \end_inset + + + + +\begin_inset Text -where -\begin_inset Formula $\bbnum 2$ -\end_inset +\begin_layout Plain Layout - denotes the -\begin_inset listings -inline true -status open +\size small +\begin_inset Formula $\left(\alpha\vee\beta\right)\wedge\gamma=\left(\alpha\wedge\gamma\right)\vee\left(\beta\wedge\gamma\right)$ +\end_inset -\begin_layout Plain Layout -Boolean \end_layout \end_inset - - type -\begin_inset Index idx -status open + + +\begin_inset Text \begin_layout Plain Layout -2@ -\begin_inset Formula $\bbnum 2$ + +\size small +\begin_inset Formula $\left(A+B\right)\times C\cong A\times C+B\times C$ \end_inset - (the -\family typewriter -Boolean -\family default - type) + \end_layout \end_inset + + + + +\begin_inset Text - (which may be defined as -\begin_inset Formula $\bbnum 2\triangleq\bbnum 1+\bbnum 1$ -\end_inset +\begin_layout Plain Layout -). -\end_layout +\size small +\begin_inset Formula $\left(\alpha\wedge\beta\right)\vee\gamma=\left(\alpha\vee\gamma\right)\wedge\left(\beta\vee\gamma\right)$ +\end_inset -\begin_layout Subsection -Type cardinalities and type equivalence -\end_layout -\begin_layout Standard -To understand why type equivalences are related to arithmetic identities, - consider the question of how many different values a given type can have. \end_layout -\begin_layout Standard -Begin by counting the number of distinct values for simple types. - For example, the -\begin_inset listings -inline true -status open +\end_inset + + +\begin_inset Text \begin_layout Plain Layout -Unit +\size small +\begin_inset Formula $\left(A\times B\right)+C\not\cong\left(A+C\right)\times\left(B+C\right)$ +\end_inset + + \end_layout \end_inset + + + - type has only one distinct value; the type -\begin_inset listings -inline true -status open +\end_inset -\begin_layout Plain Layout -Nothing \end_layout -\end_inset - - has zero values; the -\begin_inset listings -inline true -status open +\begin_layout Plain Layout +\begin_inset Caption Standard \begin_layout Plain Layout +Logic identities with disjunction and conjunction, and the possible type + equivalences. +\begin_inset CommandInset label +LatexCommand label +name "tab:Logical-identities-with-disjunction-and-conjunction" + +\end_inset + -Boolean \end_layout \end_inset - type has two distinct values ( -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -true \end_layout \end_inset - and -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -false \end_layout +\begin_layout Standard +We already verified the first line and the last three lines of Table +\begin_inset space ~ \end_inset -); and the type -\begin_inset listings -inline true -status open -\begin_layout Plain Layout - -Int -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Logical-identities-with-disjunction-and-conjunction" +plural "false" +caps "false" +noprefix "false" \end_inset - has -\begin_inset Formula $2^{32}$ +. + Other identities are verified in a similar way. + Let us begin with lines 3 and 4 of Table +\begin_inset space ~ \end_inset - distinct values. -\end_layout - -\begin_layout Standard -It is more difficult to count the number of distinct values in a type such - as -\begin_inset listings -inline true -status open -\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Logical-identities-with-disjunction-and-conjunction" +plural "false" +caps "false" +noprefix "false" -String -\end_layout +\end_inset +, which involve the proposition +\begin_inset Formula $False$ \end_inset -, which is equivalent to a list of unknown length, -\begin_inset listings -inline true + and the corresponding +\begin_inset Index idx status open \begin_layout Plain Layout - -List[Char] +void type \end_layout \end_inset -. - However, each computer's memory is limited, so there will exist a maximum - length for values of type +void type +\begin_inset Formula $\bbnum 0$ +\end_inset + + (Scala's \begin_inset listings inline true status open \begin_layout Plain Layout -String +Nothing \end_layout \end_inset +). + Reasoning about the void type needs a special technique that we will now + develop while verifying the type isomorphisms +\begin_inset Formula $\bbnum 0\times A\cong\bbnum 0$ +\end_inset + + and +\begin_inset Formula $\bbnum 0+A\cong A$ +\end_inset + . - So, the total number of possible different strings will be finite (but - will depend on the computer). \end_layout -\begin_layout Standard -For a given type -\begin_inset Formula $A$ -\end_inset +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Example-0-times-A" -, let us denote by -\begin_inset Formula $\left|A\right|$ \end_inset - the number of distinct values of type -\begin_inset Formula $A$ -\end_inset -. - The number -\begin_inset Formula $\left|A\right|$ +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Example-0-times-A" +plural "false" +caps "false" +noprefix "false" + \end_inset - is called the + \begin_inset Index idx status open \begin_layout Plain Layout -cardinality +examples (with code) \end_layout \end_inset -\series bold -cardinality -\series default - of type -\begin_inset Formula $A$ -\end_inset +\end_layout -. - This is the same as the number of elements in the set of all values of - type -\begin_inset Formula $A$ +\begin_layout Standard +Verify the type equivalence +\begin_inset Formula $\bbnum 0\times A\cong\bbnum 0$ \end_inset . - Since any computer's memory is finite, there will be -\emph on -finitely -\emph default - many different values of a given type -\begin_inset Formula $A$ -\end_inset - - that can exist in the computer. - So, we may assume that -\begin_inset Formula $\left|A\right|$ -\end_inset - - is always a finite integer value. - This assumption will simplify our reasoning. - We will not actually need to compute the precise number of, say, all the - different possible strings. - It is sufficient to know that the set of all strings is finite, so that - we can denote its cardinality by -\begin_inset Formula $|\text{String}|$ -\end_inset +\end_layout -. +\begin_layout Subparagraph +Solution \end_layout \begin_layout Standard -The next step is to consider the cardinality of types such as -\begin_inset Formula $A\times B$ +Recall that the type notation +\begin_inset Formula $\bbnum 0\times A$ \end_inset - and -\begin_inset Formula $A+B$ + represents the Scala tuple type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +(Nothing, A) +\end_layout + \end_inset . - If the types -\begin_inset Formula $A$ -\end_inset + To demonstrate that the type +\begin_inset listings +inline true +status open - and -\begin_inset Formula $B$ -\end_inset +\begin_layout Plain Layout - have cardinalities -\begin_inset Formula $\left|A\right|$ -\end_inset +(Nothing, A) +\end_layout - and -\begin_inset Formula $\left|B\right|$ \end_inset -, it follows that the set of all distinct pairs + is equivalent to the type \begin_inset listings inline true status open \begin_layout Plain Layout -(A, B) +Nothing \end_layout \end_inset - has -\begin_inset Formula $\left|A\right|\times\left|B\right|$ -\end_inset +, we need to show that the type +\begin_inset listings +inline true +status open - elements. - So, the cardinality of the type -\begin_inset Formula $A\times B$ -\end_inset +\begin_layout Plain Layout - is equal to the (arithmetic) product of the cardinalities of -\begin_inset Formula $A$ -\end_inset +(Nothing, A) +\end_layout - and -\begin_inset Formula $B$ \end_inset -. - The set of all pairs, denoted in mathematics by: -\begin_inset Formula -\[ -\left\{ (a,b)|a\in A,b\in B\right\} \quad, -\] + has +\emph on +no +\emph default + values. + Indeed, how could we create a value of type, say, +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +(Nothing, Int) +\end_layout \end_inset -is called the -\begin_inset Index idx +? We would need to fill +\emph on +both +\emph default + parts of the tuple. + We have values of type +\begin_inset listings +inline true status open \begin_layout Plain Layout -Cartesian product + +Int \end_layout \end_inset +, but we can never get a value of type +\begin_inset listings +inline true +status open -\series bold -Cartesian product -\series default - of sets -\begin_inset Formula $A$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $B$ -\end_inset +Nothing +\end_layout -, and is denoted by -\begin_inset Formula $A\times B$ \end_inset . - For this reason, the tuple type is also called the -\begin_inset Index idx + So, regardless of the type +\begin_inset listings +inline true status open \begin_layout Plain Layout -product type + +A \end_layout \end_inset - -\series bold -product type -\series default -. - Accordingly, the type notation adopts the symbol -\begin_inset Formula $\times$ -\end_inset - - for the product type. -\end_layout - -\begin_layout Standard -The set of all distinct values of the type -\begin_inset Formula $A+B$ -\end_inset - -, i.e., of the Scala type +, it is impossible to create any values of type \begin_inset listings inline true status open \begin_layout Plain Layout -Either[A, B] +(Nothing, A) \end_layout \end_inset -, is a -\begin_inset Index idx +. + In other words, the set of values of the type +\begin_inset listings +inline true status open \begin_layout Plain Layout -labeled union + +(Nothing, A) \end_layout \end_inset -labeled union of the set of values of the form + is empty. + But that is the definition of the void type \begin_inset listings inline true status open \begin_layout Plain Layout -Left(a) +Nothing \end_layout \end_inset - and the set of values of the form +. + The types \begin_inset listings inline true status open \begin_layout Plain Layout -Right(b) +(Nothing, A) \end_layout \end_inset -. - It is clear that the cardinalities of these two sets are equal to -\begin_inset Formula $\left|A\right|$ -\end_inset - - and -\begin_inset Formula $\left|B\right|$ + (denoted by +\begin_inset Formula $\bbnum 0\times A$ \end_inset - respectively. - So, the cardinality of the type +) and \begin_inset listings inline true status open \begin_layout Plain Layout -Either[A, B] +Nothing \end_layout \end_inset - is equal to -\begin_inset Formula $\left|A\right|+\left|B\right|$ + (denoted by +\begin_inset Formula $\bbnum 0$ \end_inset -. - For this reason, disjunctive types are also called -\begin_inset Index idx -status open +) are both void and therefore equivalent. +\end_layout -\begin_layout Plain Layout -sum type!see -\begin_inset Quotes eld -\end_inset +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Example-0-plus-A" -disjunctive type -\begin_inset Quotes erd \end_inset -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Example-0-plus-A" +plural "false" +caps "false" +noprefix "false" \end_inset -\series bold -sum types -\series default -, and the type notation adopts the symbol -\begin_inset Formula $+$ -\end_inset - - for these types. \end_layout \begin_layout Standard -We can write our conclusions as: -\begin_inset Formula -\begin{align*} -\left|A\times B\right| & =\left|A\right|\times\left|B\right|\quad,\\ -\left|A+B\right| & =\left|A\right|+\left|B\right|\quad. -\end{align*} - +Verify the type equivalence +\begin_inset Formula $\bbnum 0+A\cong A$ \end_inset -The type notation, -\begin_inset Formula $A\times B$ +. +\end_layout + +\begin_layout Subparagraph +Solution +\end_layout + +\begin_layout Standard +The type notation +\begin_inset Formula $\bbnum 0+A$ \end_inset - for + corresponds to the Scala type \begin_inset listings inline true status open \begin_layout Plain Layout -(A,B) +Either[Nothing, A] \end_layout \end_inset - and -\begin_inset Formula $A+B$ -\end_inset - - for +. + We need to show that any value of that type can be mapped without loss + of information to a value of type \begin_inset listings inline true status open \begin_layout Plain Layout -Either[A, B] -\end_layout - -\end_inset - -, translates directly into type cardinalities. +A \end_layout -\begin_layout Standard -The last step is to notice that two types can be equivalent, -\begin_inset Formula $P\cong Q$ \end_inset -, only if their cardinalities are equal, -\begin_inset Formula $\left|P\right|=\left|Q\right|$ +, and vice versa. + This means implementing functions +\begin_inset Formula $f_{1}:\bbnum 0+A\rightarrow A$ \end_inset -. - When the cardinalities are not equal, -\begin_inset Formula $\left|P\right|\neq\left|Q\right|$ + and +\begin_inset Formula $f_{2}:A\rightarrow\bbnum 0+A$ \end_inset -, it will be impossible to have a one-to-one correspondence between the - sets of values of type -\begin_inset Formula $P$ + such that +\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ \end_inset - and values of type -\begin_inset Formula $Q$ + and +\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ \end_inset . - So, it will be impossible to convert values from type -\begin_inset Formula $P$ -\end_inset +\end_layout - to type -\begin_inset Formula $Q$ +\begin_layout Standard +The argument of +\begin_inset Formula $f_{1}$ \end_inset - and back without loss of information. -\end_layout + is of type +\begin_inset listings +inline true +status open -\begin_layout Standard -We conclude that types are equivalent when a logical identity -\emph on -and -\emph default - an arithmetic identity hold. -\end_layout +\begin_layout Plain Layout -\begin_layout Standard -The presence of both identities does not automatically guarantee a useful - type equivalence. - The fact that information in one type can be identically stored in another - type does not necessarily mean that it is helpful to do so in a given applicati -on. +Either[Nothing, A] \end_layout -\begin_layout Standard -For example, the types +\end_inset + +. + How can we create a value of that type? Our only choices are to create + a \begin_inset listings inline true status open \begin_layout Plain Layout -Option[Option[A]] +Left(x) \end_layout \end_inset - and + with \begin_inset listings inline true status open \begin_layout Plain Layout -Either[Boolean, A] +x:Nothing \end_layout \end_inset - are equivalent because both types contain -\begin_inset Formula $2+\left|A\right|$ -\end_inset +, or to create a +\begin_inset listings +inline true +status open - distinct values. - The short notation for these types is -\begin_inset Formula $\bbnum 1+\bbnum 1+A$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $\bbnum 2+A$ -\end_inset +Right(y) +\end_layout - respectively. - The type Boolean is denoted by -\begin_inset Formula $\bbnum 2$ \end_inset - since it has only -\emph on -two -\emph default - distinct values. - -\end_layout - -\begin_layout Standard -One could write code for converting between these types without loss of - information: + with \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f1[A]: Option[Option[A]] => Either[Boolean, A] = { +y:A \end_layout -\begin_layout Plain Layout +\end_inset - case None => Left(false) // Or maybe Left(true)? -\end_layout +. + However, we cannot create a value +\begin_inset listings +inline true +status open \begin_layout Plain Layout - case Some(None) => Left(true) +x \end_layout -\begin_layout Plain Layout +\end_inset - case Some(Some(x)) => Right(x) -\end_layout + of type +\begin_inset listings +inline true +status open \begin_layout Plain Layout -} +Nothing \end_layout -\begin_layout Plain Layout +\end_inset -\end_layout + because the type +\begin_inset listings +inline true +status open \begin_layout Plain Layout -def f2[A]: Either[Boolean, A] => Option[Option[A]] = { +Nothing \end_layout -\begin_layout Plain Layout - - case Left(false) => None -\end_layout - -\begin_layout Plain Layout - - case Left(true) => Some(None) -\end_layout - -\begin_layout Plain Layout +\end_inset - case Right(x) => Some(Some(x)) -\end_layout + has +\emph on +no +\emph default + values. + We cannot create a +\begin_inset listings +inline true +status open \begin_layout Plain Layout -} +Left(x) \end_layout \end_inset -The presence of an arbitrary choice in this code is a warning sign. - In +. + The only remaining possibility is to create a \begin_inset listings inline true status open \begin_layout Plain Layout -f1 +Right(y) \end_layout \end_inset -, we could map + with some value \begin_inset listings inline true status open \begin_layout Plain Layout -None +y \end_layout \end_inset - to + of type \begin_inset listings inline true status open \begin_layout Plain Layout -Left(false) +A \end_layout \end_inset - or to +. + So, any values of type +\begin_inset Formula $\bbnum 0+A$ +\end_inset + + must be of the form \begin_inset listings inline true status open \begin_layout Plain Layout -Left(true) +Right(y) \end_layout \end_inset - and adjust the rest of the code accordingly. - The type equivalence holds with either choice. - So, these types -\emph on -are -\emph default - equivalent, but there is no -\begin_inset Quotes eld -\end_inset - -natural -\begin_inset Quotes erd -\end_inset - - choice of the conversion functions +, and we can extract that \begin_inset listings inline true status open \begin_layout Plain Layout -f1 +y \end_layout \end_inset - and + to obtain a value of type \begin_inset listings inline true status open \begin_layout Plain Layout -f2 +A \end_layout \end_inset - that will work correctly in all applications, because the meaning of these - data types will be application-dependent. - We call this type equivalence -\series bold -accidental -\series default - -\begin_inset Index idx +: +\begin_inset listings +inline false status open \begin_layout Plain Layout -type equivalence!accidental + +def f1[A]: Either[Nothing, A] => A = { \end_layout -\end_inset +\begin_layout Plain Layout -. + case Right(y) => y \end_layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Example-cardinality-option-either" +\begin_layout Plain Layout -\end_inset + // No need for `case Left(x) => ...` since no `x` can ever be given as `Left(x)`. +\end_layout +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Example-cardinality-option-either" -plural "false" -caps "false" -noprefix "false" +} +\end_layout \end_inset - -\begin_inset Index idx +For the same reason, there is only one implementation of the function +\begin_inset listings +inline true status open \begin_layout Plain Layout -solved examples + +f2 \end_layout \end_inset +: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout +def f2[A]: A => Either[Nothing, A] = { y => Right(y) } \end_layout -\begin_layout Standard -Are the types +\end_inset + +It is clear from the code that the functions \begin_inset listings inline true status open \begin_layout Plain Layout -Option[A] +f1 \end_layout \end_inset @@ -17165,165 +17246,208 @@ status open \begin_layout Plain Layout -Either[Unit, A] +f2 \end_layout \end_inset - equivalent? Check whether the corresponding logic identity and arithmetic - identity hold. + are inverses of each other. \end_layout -\begin_layout Paragraph -Solution +\begin_layout Standard +We have just seen that a value of type +\begin_inset Formula $\bbnum 0+A$ +\end_inset + + is always a +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Right(y) \end_layout -\begin_layout Standard -Begin by writing the given types in the type notation: +\end_inset + + with some \begin_inset listings inline true status open \begin_layout Plain Layout -Option[A] +y:A \end_layout \end_inset - is written as -\begin_inset Formula $\bbnum 1+A$ +. + Similarly, a value of type +\begin_inset Formula $A+\bbnum 0$ \end_inset -, and + is always a \begin_inset listings inline true status open \begin_layout Plain Layout -Either[Unit, A] +Left(x) \end_layout \end_inset - is written also as -\begin_inset Formula $\bbnum 1+A$ + with some +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +x:A +\end_layout + \end_inset . - The notation already indicates that the types are equivalent. - But let us verify explicitly that the type notation is not misleading us - here. -\end_layout + So, we will use the notation +\begin_inset Formula $A+\bbnum 0$ +\end_inset -\begin_layout Standard -To establish the type equivalence, we need to implement two fully parametric - functions: + and +\begin_inset Formula $\bbnum 0+A$ +\end_inset + + to +\emph on +denote +\emph default + the \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f1[A]: Option[A] => Either[Unit, A] = ??? +Left \end_layout +\end_inset + + and the +\begin_inset listings +inline true +status open + \begin_layout Plain Layout -def f2[A]: Either[Unit, A] => Option[A] = ??? +Right \end_layout \end_inset -These functions must satisfy -\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ -\end_inset + parts of the disjunctive type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Either +\end_layout - and -\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ \end_inset . - It is straightforward to implement + This notation agrees with the behavior of the Scala compiler, which will + infer the types \begin_inset listings inline true status open \begin_layout Plain Layout -f1 +Either[A, Nothing] \end_layout \end_inset - and +or \begin_inset listings inline true status open \begin_layout Plain Layout -f2 +Either[Nothing, A] \end_layout \end_inset -: + for these parts: \begin_inset listings inline false status open \begin_layout Plain Layout -def f1[A]: Option[A] => Either[Unit, A] = { +def toLeft[A, B]: A => Either[A, B] = x => Left(x) \end_layout \begin_layout Plain Layout - case None => Left(()) +def toRight[A, B]: B => Either[A, B] = y => Right(y) \end_layout \begin_layout Plain Layout - case Some(x) => Right(x) \end_layout \begin_layout Plain Layout -} +scala> toLeft(123) \end_layout \begin_layout Plain Layout -def f2[A]: Either[Unit, A] => Option[A] = { +res0: Either[Int, Nothing] = Left(123) \end_layout \begin_layout Plain Layout - case Left(()) => None \end_layout \begin_layout Plain Layout - case Right(x) => Some(x) +scala> toRight( +\begin_inset Quotes eld +\end_inset + +abc +\begin_inset Quotes erd +\end_inset + +) \end_layout \begin_layout Plain Layout -} +res1: Either[Nothing, String] = Right("abc") \end_layout \end_inset -The code clearly shows that +We can write the functions \begin_inset listings inline true status open \begin_layout Plain Layout -f1 +toLeft \end_layout \end_inset @@ -17335,961 +17459,1135 @@ status open \begin_layout Plain Layout -f2 +toRight \end_layout \end_inset - are inverses of each other. - This verifies the type equivalence. -\end_layout + in a code notation as: +\begin_inset Formula +\[ +\text{toLeft}^{A,B}\triangleq x^{:A}\rightarrow x+\bbnum 0^{:B}\quad,\quad\quad\text{toRight}^{A,B}\triangleq y^{:B}\rightarrow\bbnum 0^{:A}+y\quad. +\] -\begin_layout Standard -The logic identity is -\begin_inset Formula $True\vee A=True\vee A$ \end_inset - and holds trivially. - It remains to check the arithmetic identity, which relates the cardinalities - of types +In this notation, a value of the disjunctive type is shown without using + Scala class names such as \begin_inset listings inline true status open \begin_layout Plain Layout -Option[A] +Either \end_layout \end_inset - and +, \begin_inset listings inline true status open \begin_layout Plain Layout -Either[Unit, A] +Right \end_layout \end_inset -. - Assume that the cardinality of type +, and \begin_inset listings inline true status open \begin_layout Plain Layout -A +Left \end_layout -\end_inset - - is -\begin_inset Formula $\left|A\right|$ \end_inset . - Any possible value of type -\begin_inset listings -inline true -status open + This shortens the writing and speeds up code reasoning. + +\end_layout -\begin_layout Plain Layout +\begin_layout Standard +The type annotation +\begin_inset Formula $\bbnum 0^{:A}$ +\end_inset -Option[A] -\end_layout + is helpful to remind ourselves about the type parameter +\begin_inset Formula $A$ +\end_inset + used, e.g., by the disjunctive value +\begin_inset Formula $\bbnum 0^{:A}+y^{:B}$ \end_inset - must be either + in the body of \begin_inset listings inline true status open \begin_layout Plain Layout -None +toRight[A, B] \end_layout \end_inset - or +. + Without this type annotation, +\begin_inset Formula $\bbnum 0+y^{:B}$ +\end_inset + + means a value of type \begin_inset listings inline true status open \begin_layout Plain Layout -Some(x) +Either[A, B] \end_layout \end_inset -, where -\begin_inset listings -inline true -status open + where the parameter +\begin_inset Formula $A$ +\end_inset -\begin_layout Plain Layout + should be determined by matching the types of other expressions. + When it is clear what types are being used, we may omit type annotations + and write simply +\begin_inset Formula $\bbnum 0+y$ +\end_inset -x + instead of +\begin_inset Formula $\bbnum 0^{:A}+y^{:B}$ +\end_inset + +. \end_layout +\begin_layout Standard +In the notation +\begin_inset Formula $\bbnum 0+y^{:B}$ \end_inset - is a value of type -\begin_inset listings -inline true -status open +, we use the symbol +\begin_inset Formula $\bbnum 0$ +\end_inset -\begin_layout Plain Layout + rather than an ordinary zero ( +\begin_inset Formula $0$ +\end_inset -A -\end_layout +), to avoid suggesting that +\begin_inset Formula $0$ +\end_inset + is a value of type +\begin_inset Formula $\bbnum 0$ \end_inset . - So, the number of distinct values of type + The void type +\begin_inset Formula $\bbnum 0$ +\end_inset + + has +\emph on +no +\emph default + values, unlike the \begin_inset listings inline true status open \begin_layout Plain Layout -Option[A] +Unit \end_layout \end_inset - is -\begin_inset Formula $1+\left|A\right|$ + type, +\begin_inset Formula $\bbnum 1$ \end_inset -. - All possible values of type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout +, which has a value denoted by +\begin_inset Formula $1$ +\end_inset -Either[Unit, A] + in the code notation. \end_layout +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Example-1xA" + \end_inset - are of the form -\begin_inset listings -inline true -status open -\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Example-1xA" +plural "false" +caps "false" +noprefix "false" + +\end_inset + -Left(()) \end_layout +\begin_layout Standard +Verify the type equivalence +\begin_inset Formula $A\times\bbnum 1\cong A$ \end_inset - or +. +\end_layout + +\begin_layout Subparagraph +Solution +\end_layout + +\begin_layout Standard +The corresponding Scala types are the tuple \begin_inset listings inline true status open \begin_layout Plain Layout -Right(x) +(A, Unit) \end_layout \end_inset -, where + and the type \begin_inset listings inline true status open \begin_layout Plain Layout -x +A \end_layout \end_inset - is a value of type +. + We need to implement functions +\begin_inset Formula $f_{1}:\forall A.\,A\times\bbnum 1\rightarrow A$ +\end_inset + + and +\begin_inset Formula $f_{2}:\forall A.\,A\rightarrow A\times\bbnum 1$ +\end_inset + + and to demonstrate that they are inverses of each other. + The Scala code for these functions is: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -A +def f1[A]: ((A, Unit)) => A = { case (a, ()) => a } +\end_layout + +\begin_layout Plain Layout + +def f2[A]: A => (A, Unit) = { a => (a, ()) } \end_layout \end_inset -. - So, the cardinality of type +Let us first write a proof by reasoning directly with Scala code: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -Either[Unit, A] +(f1 andThen f2)((a,())) == f2(f1((a,())) == f2(a) == (a, ()) +\end_layout + +\begin_layout Plain Layout + +(f2 andThen f1)(a) == f1(f2(a)) == f1((a, ())) = a \end_layout \end_inset - is -\begin_inset Formula $1+\left|A\right|$ +Now let us write a proof in the code notation. + The codes of +\begin_inset Formula $f_{1}$ \end_inset -. - We see that the arithmetic identity holds: the types + and +\begin_inset Formula $f_{2}$ +\end_inset + + are: +\begin_inset Formula +\[ +f_{1}\triangleq a^{:A}\times1\rightarrow a\quad,\quad\quad f_{2}\triangleq a^{:A}\rightarrow a\times1\quad, +\] + +\end_inset + +where we denoted by +\begin_inset Formula $1$ +\end_inset + + the value \begin_inset listings inline true status open \begin_layout Plain Layout -Option[A] +() \end_layout \end_inset - and + of the \begin_inset listings inline true status open \begin_layout Plain Layout -Either[Unit, A] +Unit \end_layout \end_inset - have equally many distinct values. -\end_layout + type. + We find: +\begin_inset Formula +\begin{align*} +(f_{1}\bef f_{2})(a^{:A}\times1) & =f_{2}\left(f_{1}(a\times1)\right)=f_{2}\left(a\right)=a\times1\quad,\\ +(f_{2}\bef f_{1})(a^{:A}) & =f_{1}(f_{2}(a))=f_{1}(a\times1)=a\quad. +\end{align*} -\begin_layout Standard -This example shows that the type notation is helpful for reasoning about - type equivalences. - The answer was found immediately when we wrote the type notation ( -\begin_inset Formula $\bbnum 1+A$ \end_inset -) for the given types. -\end_layout - -\begin_layout Subsection -Type equivalence involving function types -\end_layout +This shows that both compositions are identity functions. + Another way of writing the proof is by computing the function compositions + symbolically, without applying to a value +\begin_inset Formula $a^{:A}$ +\end_inset -\begin_layout Standard -Until now, we have looked at product types and disjunctive types. - We now turn to type constructions involving function types. -\end_layout +: +\begin_inset Formula +\begin{align*} +f_{1}\bef f_{2} & =\left(a\times1\rightarrow a\right)\bef\left(a\rightarrow a\times1\right)=\left(a\times1\rightarrow a\times1\right)=\text{id}^{A\times\bbnum 1}\quad,\\ +f_{2}\bef f_{1} & =\left(a\rightarrow a\times1\right)\bef\left(a\times1\rightarrow a\right)=\left(a\rightarrow a\right)=\text{id}^{A}\quad. +\end{align*} -\begin_layout Standard -Consider two types -\begin_inset Formula $A$ \end_inset - and -\begin_inset Formula $B$ + +\end_layout + +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Example-A+B" + \end_inset - having known cardinalities -\begin_inset Formula $\left|A\right|$ + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Example-A+B" +plural "false" +caps "false" +noprefix "false" + \end_inset - and -\begin_inset Formula $\left|B\right|$ + +\end_layout + +\begin_layout Standard +Verify the type equivalence +\begin_inset Formula $A+B\cong B+A$ \end_inset . - How many distinct values does the function type -\begin_inset Formula $A\rightarrow B$ -\end_inset +\end_layout - have? A function +\begin_layout Subparagraph +Solution +\end_layout + +\begin_layout Standard +The corresponding Scala types are \begin_inset listings inline true status open \begin_layout Plain Layout -f: A => B +Either[A, B] \end_layout \end_inset - needs to select a value of type -\begin_inset Formula $B$ -\end_inset - - for each possible value of type -\begin_inset Formula $A$ -\end_inset - -. - Therefore, the number of different functions + and \begin_inset listings inline true status open \begin_layout Plain Layout -f: A => B +Either[B, A] \end_layout -\end_inset - - is -\begin_inset Formula $\left|B\right|^{\left|A\right|}$ \end_inset . - + Use pattern matching to implement the functions required for the type equivalen +ce: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def f1[A, B]: Either[A, B] => Either[B, A] = { \end_layout -\begin_layout Standard -Here -\begin_inset Formula $\left|B\right|^{\left|A\right|}$ -\end_inset +\begin_layout Plain Layout - denotes the -\series bold -numeric exponent -\series default + case Left(a) => Right(a) // No other choice here. +\end_layout -\begin_inset Index idx -status open +\begin_layout Plain Layout + + case Right(b) => Left(b) // No other choice here. +\end_layout \begin_layout Plain Layout -exponent + +} \end_layout -\end_inset +\begin_layout Plain Layout -, that is, -\begin_inset Formula $\left|B\right|$ -\end_inset +def f2[A, B]: Either[B, A] => Either[A, B] = f1[B, A] +\end_layout - to the power -\begin_inset Formula $\left|A\right|$ \end_inset -. - We use the numeric exponent notation ( -\begin_inset Formula $a^{b}$ -\end_inset +The functions +\begin_inset listings +inline true +status open -) only when computing with numbers. - When denoting types and code, this book uses superscripts for type parameters - and type annotations. +\begin_layout Plain Layout + +f1 \end_layout -\begin_layout Standard -For the types -\begin_inset Formula $A=B=\text{Int}$ \end_inset -, we have -\begin_inset Formula $\left|A\right|=\left|B\right|=2^{32}$ -\end_inset + and +\begin_inset listings +inline true +status open -, and the exponential formula gives: -\begin_inset Formula -\[ -\left|A\rightarrow B\right|=(2^{32})^{\left(2^{32}\right)}=2^{32\times2^{32}}=2^{2^{37}}\approx10^{4.1\times10^{10}}\quad. -\] +\begin_layout Plain Layout + +f2 +\end_layout \end_inset -This number greatly exceeds the number of atoms in the observable Universe! -\begin_inset Foot + are implemented by code that can be derived unambiguously from the type + signatures. + For instance, the line +\begin_inset listings +inline true status open \begin_layout Plain Layout -Estimated to be between -\begin_inset Formula $10^{78}$ -\end_inset - and -\begin_inset Formula $10^{82}$ +case Left(a) => ... +\end_layout + \end_inset -, see -\family typewriter + is required to return a value of type +\begin_inset listings +inline true +status open -\begin_inset CommandInset href -LatexCommand href -target "https://www.universetoday.com/36302/atoms-in-the-universe/amp/" -literal "false" +\begin_layout Plain Layout + +Either[B, A] +\end_layout \end_inset + by using a given value +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +a:A \end_layout \end_inset - However, almost all of those functions will map integers to integers in - extremely complicated (and practically useless) ways. - The code of those functions will be much larger than the available memory - of a realistic computer. - So, the number of practically implementable functions of type -\begin_inset Formula $A\rightarrow B$ -\end_inset +. + The only way of doing that is by returning +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Right(a) +\end_layout - can be much smaller than -\begin_inset Formula $\left|B\right|^{\left|A\right|}$ \end_inset . - Since the code of a function is a string of bytes that needs to fit into - the computer's memory, the number of implementable functions is no larger - than the number of possible byte strings. \end_layout \begin_layout Standard -Nevertheless, the formula -\begin_inset Formula $\left|B\right|^{\left|A\right|}$ -\end_inset +It is clear from the code that the functions +\begin_inset listings +inline true +status open - is useful since it shows the number of distinct functions that are possible - in principle. - When types -\begin_inset Formula $A$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $B$ -\end_inset +f1 +\end_layout - have only a small number of distinct values (for example, -\begin_inset Formula $A=$ \end_inset - + and \begin_inset listings inline true status open \begin_layout Plain Layout -Option[Boolean]] +f2 \end_layout \end_inset - and -\begin_inset Formula $B=$ -\end_inset - - + are inverses of each other. + To verify that rigorously, we need to show that \begin_inset listings inline true status open \begin_layout Plain Layout -Either[Boolean, Boolean] +f1 andThen f2 \end_layout \end_inset -), the formula -\begin_inset Formula $\left|B\right|^{\left|A\right|}$ -\end_inset + is equal to an identity function. + The function +\begin_inset listings +inline true +status open - gives an exact and practically relevant answer. +\begin_layout Plain Layout + +f1 andThen f2 \end_layout -\begin_layout Standard -Let us now look for logic identities and arithmetic identities involving - function types. - Table -\begin_inset space ~ \end_inset + applies +\begin_inset listings +inline true +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Logical-identities-with-function-types" -plural "false" -caps "false" -noprefix "false" - -\end_inset +\begin_layout Plain Layout - lists the available identities and the corresponding type equivalences. - (In the last column, we defined -\begin_inset Formula $a\triangleq\left|A\right|$ -\end_inset +f2 +\end_layout -, -\begin_inset Formula $b\triangleq\left|B\right|$ \end_inset -, and -\begin_inset Formula $c\triangleq\left|C\right|$ -\end_inset + to the result of +\begin_inset listings +inline true +status open - for brevity.) -\end_layout +\begin_layout Plain Layout -\begin_layout Standard -It is notable that no logic identity is available for the formula -\begin_inset Formula $\alpha\Rightarrow\left(\beta\vee\gamma\right)$ -\end_inset +f1 +\end_layout -, and correspondingly no type equivalence is available for the type expression - -\begin_inset Formula $A\rightarrow B+C$ \end_inset - (although there is an identity for -\begin_inset Formula $A\rightarrow B\times C$ -\end_inset +. + The code of +\begin_inset listings +inline true +status open -). - Reasoning about types of the form -\begin_inset Formula $A\rightarrow B+C$ -\end_inset +\begin_layout Plain Layout - is more complicated because those types usually cannot be transformed into - simpler types. +f1 \end_layout -\begin_layout Standard -\begin_inset Float table -wide false -sideways false -status open +\end_inset -\begin_layout Plain Layout -\align center -\begin_inset Tabular - - - - - - - -\begin_inset Text + contains two +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\series bold -\size small -Logical identity (if holds) +case ... \end_layout \end_inset - - -\begin_inset Text + + lines, each returning a result. + So, we need to apply +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\series bold -\size small -Type equivalence +f2 \end_layout \end_inset - - -\begin_inset Text + + separately in each line. + Evaluate the code symbolically: +\begin_inset listings +inline false +status open \begin_layout Plain Layout -\series bold -\size small -Arithmetic identity +(f1 andThen f2) == { \end_layout -\end_inset - - - - -\begin_inset Text - \begin_layout Plain Layout -\size small -\begin_inset Formula $\left(True\Rightarrow\alpha\right)=\alpha$ -\end_inset + case Left(a) => f2(Right(a)) +\end_layout +\begin_layout Plain Layout + case Right(b) => f2(Left(b)) \end_layout -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout -\size small -\begin_inset Formula $\bbnum 1\rightarrow A\cong A$ -\end_inset +} == { +\end_layout +\begin_layout Plain Layout + case Left(a) => Left(a) \end_layout -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout -\size small -\begin_inset Formula $a^{1}=a$ -\end_inset + case Right(b) => Right(b) +\end_layout +\begin_layout Plain Layout +} \end_layout \end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $\left(False\Rightarrow\alpha\right)=True$ -\end_inset +The result is a function of type +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +Either[A, B] => Either[A, B] \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + that does not change its argument; so, it is equal to the identity function. + +\end_layout -\size small -\begin_inset Formula $\bbnum 0\rightarrow A\cong\bbnum 1$ -\end_inset +\begin_layout Standard +Let us now write the function +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +f1 \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + in the code notation and perform the same derivation. + We will also develop a useful notation for functions operating on disjunctive + types. +\end_layout -\size small -\begin_inset Formula $a^{0}=1$ -\end_inset +\begin_layout Standard +The pattern matching construction in the Scala code of +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +f1 \end_layout \end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $\left(\alpha\Rightarrow True\right)=True$ -\end_inset + is similar to a pair of functions with types +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +A => Either[B, A] \end_layout \end_inset - - -\begin_inset Text + + and +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\size small -\begin_inset Formula $A\rightarrow\bbnum 1\cong\bbnum 1$ +B => Either[B, A] +\end_layout + \end_inset +. + One of these functions is applied depending on whether the argument of + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +f1 \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + has type +\begin_inset Formula $A+\bbnum 0$ +\end_inset -\size small -\begin_inset Formula $1^{a}=1$ + or +\begin_inset Formula $\bbnum 0+B$ \end_inset +. + So, we may write the code of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +f1 \end_layout \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout + as: +\begin_inset Formula +\[ +f_{1}\triangleq x^{:A+B}\rightarrow\begin{cases} +\text{if }x=a^{:A}+\bbnum 0^{:B}\quad: & \bbnum 0^{:B}+a^{:A}\\ +\text{if }x=\bbnum 0^{:A}+b^{:B}\quad: & b^{:B}+\bbnum 0^{:A} +\end{cases} +\] -\size small -\begin_inset Formula $\left(\alpha\Rightarrow False\right)\neq False$ \end_inset - -\end_layout - +Since both the argument and the result of +\begin_inset Formula $f_{1}$ \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $A\rightarrow\bbnum 0\not\cong\bbnum 0$ + are disjunctive types with +\begin_inset Formula $2$ \end_inset - -\end_layout - + parts each, it is convenient to write the code of +\begin_inset Formula $f_{1}$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout - -\size small -\begin_inset Formula $0^{a}\neq0$ + as a +\begin_inset Formula $2\times2$ \end_inset + +\emph on +matrix +\emph default + that maps the input parts to the output parts: +\begin_inset Index idx +status open +\begin_layout Plain Layout +disjunctive type!in matrix notation \end_layout \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout - -\size small -\begin_inset Formula $\left(\alpha\vee\beta\right)\Rightarrow\gamma=\left(\alpha\Rightarrow\gamma\right)\wedge\left(\beta\Rightarrow\gamma\right)$ -\end_inset +\begin_inset Index idx +status open +\begin_layout Plain Layout +matrix notation \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\size small -\begin_inset Formula $A+B\rightarrow C\cong\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)$ -\end_inset +\begin_inset listings +inline false +status open +\begin_layout Plain Layout +def f1[A, B]: Either[A, B] => Either[B, A] = { \end_layout -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout -\size small -\begin_inset Formula $c^{a+b}=c^{a}\times c^{b}$ -\end_inset - - + case Left(a) => Right(a) \end_layout -\end_inset - - - - -\begin_inset Text - \begin_layout Plain Layout -\size small -\begin_inset Formula $(\alpha\wedge\beta)\Rightarrow\gamma=\alpha\Rightarrow\left(\beta\Rightarrow\gamma\right)$ -\end_inset + case Right(b) => Left(b) +\end_layout +\begin_layout Plain Layout +} \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout -\size small -\begin_inset Formula $A\times B\rightarrow C\cong A\rightarrow B\rightarrow C$ +\begin_inset Formula +\[ +f_{1}\triangleq\,\begin{array}{|c||cc|} + & B & A\\ +\hline A & \bbnum 0 & a^{:A}\rightarrow a\\ +B & b^{:B}\rightarrow b & \bbnum 0 +\end{array}\quad. +\] + \end_inset +The rows of the matrix correspond to the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +case \end_layout \end_inset - - -\begin_inset Text + + rows in the Scala code. + There is one row for each part of the disjunctive type of the argument. + The columns of the matrix correspond to the parts of the disjunctive type + of the result. +\begin_inset Index idx +status open \begin_layout Plain Layout +pattern matching!in matrix notation +\end_layout -\size small -\begin_inset Formula $c^{a\times b}=(c^{b})^{a}$ \end_inset - -\end_layout - + The matrix element in row +\begin_inset Formula $A$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout + and column +\begin_inset Formula $A$ +\end_inset -\size small -\begin_inset Formula $\alpha\Rightarrow\left(\beta\wedge\gamma\right)=\left(\alpha\Rightarrow\beta\right)\wedge\left(\alpha\Rightarrow\gamma\right)$ + is a function of type +\begin_inset Formula $A\rightarrow A$ \end_inset + that corresponds to the line +\begin_inset listings +inline true +status open +\begin_layout Plain Layout + +case Left(a) => Right(a) \end_layout \end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $A\rightarrow B\times C\cong\left(A\rightarrow B\right)\times\left(A\rightarrow C\right)$ + in the Scala code. + The matrix element in row +\begin_inset Formula $A$ \end_inset + and column +\begin_inset Formula $B$ +\end_inset -\end_layout - + is written as +\begin_inset Formula $\bbnum 0$ \end_inset - - -\begin_inset Text + + because no value of that type is returned. + In this way, we translate each line of the +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\size small -\begin_inset Formula $\left(b\times c\right)^{a}=b^{a}\times c^{a}$ -\end_inset +match / case +\end_layout +\end_inset + expression into a code matrix. \end_layout +\begin_layout Standard +The code of +\begin_inset Formula $f_{2}$ \end_inset - - - + is written similarly. + Let us rename arguments for clarity: +\begin_inset space \hfill{} \end_inset -\end_layout +\begin_inset listings +inline false +status open \begin_layout Plain Layout -\begin_inset Caption Standard + +def f2[A, B]: Either[B, A] => Either[A, B] = { +\end_layout \begin_layout Plain Layout -Logical identities with implication, and the corresponding type equivalences - and arithmetic identities. -\begin_inset CommandInset label -LatexCommand label -name "tab:Logical-identities-with-function-types" -\end_inset + case Left(y) => Right(y) +\end_layout +\begin_layout Plain Layout + case Right(x) => Left(x) \end_layout -\end_inset - +\begin_layout Plain Layout +} \end_layout \end_inset -\end_layout +\begin_inset Formula +\[ +f_{2}\triangleq\,\begin{array}{|c||cc|} + & A & B\\ +\hline B & \bbnum 0 & y^{:B}\rightarrow y\\ +A & x^{:A}\rightarrow x & \bbnum 0 +\end{array}\quad. +\] -\begin_layout Standard -We will now prove some of the type identities in Table -\begin_inset space ~ \end_inset +The forward composition +\begin_inset Formula $f_{1}\bef f_{2}$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Logical-identities-with-function-types" -plural "false" -caps "false" -noprefix "false" + is computed by the standard rules of row-by-column matrix multiplication. +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://en.wikipedia.org/wiki/Matrix_multiplication" \end_inset -. + +\end_layout + +\end_inset + + Any terms containing +\begin_inset Formula $\bbnum 0$ +\end_inset + + are omitted, and the remaining functions are composed: +\begin_inset Formula +\begin{align*} +f_{1}\bef f_{2} & =\,\begin{array}{|c||cc|} + & B & A\\ +\hline A & \bbnum 0 & a^{:A}\rightarrow a\\ +B & b^{:B}\rightarrow b & \bbnum 0 +\end{array}\,\bef\,\begin{array}{|c||cc|} + & A & B\\ +\hline B & \bbnum 0 & y^{:B}\rightarrow y\\ +A & x^{:A}\rightarrow x & \bbnum 0 +\end{array}\\ +\text{matrix composition}:\quad & =\,\,\begin{array}{|c||cc|} + & A & B\\ +\hline A & \gunderline{(a^{:A}\rightarrow a)\bef(x^{:A}\rightarrow x)} & \bbnum 0\\ +B & \bbnum 0 & \gunderline{(b^{:B}\rightarrow b)\bef(y^{:B}\rightarrow y)} +\end{array}\\ +\text{function composition}:\quad & =\,\begin{array}{|c||cc|} + & A & B\\ +\hline A & \text{id} & \bbnum 0\\ +B & \bbnum 0 & \text{id} +\end{array}\,=\text{id}^{:A+B\rightarrow A+B}\quad. +\end{align*} + +\end_inset + + +\end_layout + +\begin_layout Standard +Several features of the matrix notation are helpful in such calculations. + The parts of the code of +\begin_inset Formula $f_{1}$ +\end_inset + + are automatically composed with the corresponding parts of the code of + +\begin_inset Formula $f_{2}$ +\end_inset + +. + To check that the types match in the function composition, we just need + to compare the types in the output row +\begin_inset Formula $\,\begin{array}{||cc|} +B & A\end{array}\,$ +\end_inset + + of +\begin_inset Formula $f_{1}$ +\end_inset + + with the input column +\begin_inset Formula $\,\begin{array}{|c||} +B\\ +A +\end{array}\,$ +\end_inset + + of +\begin_inset Formula $f_{2}$ +\end_inset + +. + Once we verified that all types match, we may omit the type annotations + and write the same derivation more concisely as: +\begin_inset Formula +\begin{align*} +f_{1}\bef f_{2} & =\,\begin{array}{||cc|} +\bbnum 0 & a^{:A}\rightarrow a\\ +b^{:B}\rightarrow b & \bbnum 0 +\end{array}\,\bef\,\begin{array}{||cc|} +\bbnum 0 & y^{:B}\rightarrow y\\ +x^{:A}\rightarrow x & \bbnum 0 +\end{array}\\ +\text{matrix composition}:\quad & =\,\,\begin{array}{||cc|} +\gunderline{(a^{:A}\rightarrow a)\bef(x^{:A}\rightarrow x)} & \bbnum 0\\ +\bbnum 0 & \gunderline{(b^{:B}\rightarrow b)\bef(y^{:B}\rightarrow y)} +\end{array}\\ +\text{function composition}:\quad & =\,\begin{array}{||cc|} +\text{id} & \bbnum 0\\ +\bbnum 0 & \text{id} +\end{array}\,=\text{id}\quad. +\end{align*} + +\end_inset + +The identity function is represented by the diagonal matrix: +\begin_inset Formula $\,\begin{array}{||cc|} +\text{id} & \bbnum 0\\ +\bbnum 0 & \text{id} +\end{array}$ +\end_inset + + +\begin_inset space ~ +\end_inset + +. \end_layout \begin_layout Subsubsection -Example +Exercise \begin_inset CommandInset label LatexCommand label -name "subsec:ch-Example-type-identity-f" +name "subsec:ch-Exercise-AxB" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-Example-type-identity-f" +reference "subsec:ch-Exercise-AxB" plural "false" caps "false" noprefix "false" @@ -18301,7 +18599,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +exercises \end_layout \end_inset @@ -18311,1875 +18609,2064 @@ solved examples \begin_layout Standard Verify the type equivalence -\begin_inset Formula $\bbnum 1\rightarrow A\cong A$ +\begin_inset Formula $A\times B\cong B\times A$ \end_inset . \end_layout -\begin_layout Subparagraph -Solution +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-A+B+C" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Exercise-A+B+C" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + \end_layout \begin_layout Standard -Recall that the type notation -\begin_inset Formula $\bbnum 1\rightarrow A$ +Verify the type equivalence +\begin_inset Formula $\left(A+B\right)+C\cong A+\left(B+C\right)$ \end_inset - means the Scala function type -\begin_inset listings -inline true -status open +. + Since Section +\begin_inset space ~ +\end_inset -\begin_layout Plain Layout -Unit => A +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Logical-identity-not-type-equivalence" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + proved the equivalences +\begin_inset Formula $\left(A+B\right)+C\cong A+\left(B+C\right)$ +\end_inset + + and +\begin_inset Formula $\left(A\times B\right)\times C\cong A\times\left(B\times C\right)$ +\end_inset + +, we may write +\begin_inset Formula $A+B+C$ +\end_inset + + and +\begin_inset Formula $A\times B\times C$ +\end_inset + + without any parentheses. \end_layout +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-A+B2" + \end_inset -. - There is only one value of type -\begin_inset listings -inline true -status open -\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Exercise-A+B2" +plural "false" +caps "false" +noprefix "false" + +\end_inset + -Unit \end_layout +\begin_layout Standard +Verify the type equivalence: +\begin_inset Formula +\[ +\left(A+B\right)\times\left(A+B\right)=A\times A+\bbnum 2\times A\times B+B\times B\quad, +\] + \end_inset -. - The choice of a function of type +where +\begin_inset Formula $\bbnum 2$ +\end_inset + + denotes the \begin_inset listings inline true status open \begin_layout Plain Layout -Unit => A +Boolean \end_layout \end_inset - is the same as the choice of a value of type -\begin_inset listings -inline true + type +\begin_inset Index idx status open \begin_layout Plain Layout +2@ +\begin_inset Formula $\bbnum 2$ +\end_inset -A + (the +\family typewriter +Boolean +\family default + type) \end_layout \end_inset -. - So, the type -\begin_inset Formula $\bbnum 1\rightarrow A$ + (which may be defined as +\begin_inset Formula $\bbnum 2\triangleq\bbnum 1+\bbnum 1$ \end_inset - has -\begin_inset Formula $\left|A\right|$ -\end_inset +). +\end_layout - distinct values, and the arithmetic identity holds. +\begin_layout Subsection +Type cardinalities and type equivalence \end_layout \begin_layout Standard -To verify the type equivalence explicitly, we need to implement two functions: +To understand why type equivalences are related to arithmetic identities, + consider the question of how many different values a given type can have. +\end_layout + +\begin_layout Standard +Begin by counting the number of distinct values for simple types. + For example, the \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f1[A]: (Unit => A) => A = ??? -\end_layout - -\begin_layout Plain Layout - -def f2[A]: A => Unit => A = ??? +Unit \end_layout \end_inset -The first function needs to produce a value of type + type has only one distinct value; the type \begin_inset listings inline true status open \begin_layout Plain Layout -A +Nothing \end_layout \end_inset -, given an argument of the function type + has zero values; the \begin_inset listings inline true status open \begin_layout Plain Layout -Unit => A +Boolean \end_layout \end_inset -. - The only possibility is to apply that function to the value of type + type has two distinct values ( \begin_inset listings inline true status open \begin_layout Plain Layout -Unit +true \end_layout \end_inset -. - We can always produce that value as + and \begin_inset listings inline true status open \begin_layout Plain Layout -() +false \end_layout \end_inset -: +); and the type \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f1[A]: (Unit => A) => A = (h: Unit => A) => h(()) +Int \end_layout \end_inset -Implementing + has +\begin_inset Formula $2^{32}$ +\end_inset + + distinct values. +\end_layout + +\begin_layout Standard +It is more difficult to count the number of distinct values in a type such + as \begin_inset listings inline true status open \begin_layout Plain Layout -f2 +String \end_layout \end_inset - is straightforward. - We can just discard the +, which is equivalent to a list of unknown length, \begin_inset listings inline true status open \begin_layout Plain Layout -Unit +List[Char] \end_layout \end_inset - argument: +. + However, each computer's memory is limited, so there will exist a maximum + length for values of type \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f2[A]: A => Unit => A = (x: A) => _ => x +String \end_layout \end_inset -It remains to show that the functions -\begin_inset listings -inline true -status open +. + So, the total number of possible different strings will be finite (but + will depend on the computer). +\end_layout -\begin_layout Plain Layout +\begin_layout Standard +For a given type +\begin_inset Formula $A$ +\end_inset -f1 -\end_layout +, let us denote by +\begin_inset Formula $\left|A\right|$ +\end_inset + the number of distinct values of type +\begin_inset Formula $A$ \end_inset - and -\begin_inset listings -inline true +. + The number +\begin_inset Formula $\left|A\right|$ +\end_inset + + is called the +\begin_inset Index idx status open \begin_layout Plain Layout - -f2 +cardinality \end_layout \end_inset - are inverses of each other. - Let us show the proof using Scala code and then using the code notation. -\end_layout - -\begin_layout Standard -Writing Scala code, compute -\begin_inset listings -inline true -status open -\begin_layout Plain Layout +\series bold +cardinality +\series default + of type +\begin_inset Formula $A$ +\end_inset -f1(f2(x)) -\end_layout +. + This is the same as the number of elements in the set of all values of + type +\begin_inset Formula $A$ +\end_inset +. + Since any computer's memory is finite, there will be +\emph on +finitely +\emph default + many different values of a given type +\begin_inset Formula $A$ \end_inset - for an arbitrary -\begin_inset listings -inline true -status open + that can exist in the computer. + So, we may assume that +\begin_inset Formula $\left|A\right|$ +\end_inset -\begin_layout Plain Layout + is always a finite integer value. + This assumption will simplify our reasoning. + We will not actually need to compute the precise number of, say, all the + different possible strings. + It is sufficient to know that the set of all strings is finite, so that + we can denote its cardinality by +\begin_inset Formula $|\text{String}|$ +\end_inset -x:A +. \end_layout +\begin_layout Standard +The next step is to consider the cardinality of types such as +\begin_inset Formula $A\times B$ \end_inset -. - Using the code of -\begin_inset listings -inline true -status open + and +\begin_inset Formula $A+B$ +\end_inset -\begin_layout Plain Layout +. + If the types +\begin_inset Formula $A$ +\end_inset -f1 -\end_layout + and +\begin_inset Formula $B$ +\end_inset + have cardinalities +\begin_inset Formula $\left|A\right|$ \end_inset and +\begin_inset Formula $\left|B\right|$ +\end_inset + +, it follows that the set of all distinct pairs \begin_inset listings inline true status open \begin_layout Plain Layout -f2 +(A, B) \end_layout \end_inset -, we get: -\begin_inset listings -inline false -status open + has +\begin_inset Formula $\left|A\right|\times\left|B\right|$ +\end_inset -\begin_layout Plain Layout + elements. + So, the cardinality of the type +\begin_inset Formula $A\times B$ +\end_inset -f1(f2(x)) == f1(_ => x) == (_ => x)(()) == x -\end_layout + is equal to the (arithmetic) product of the cardinalities of +\begin_inset Formula $A$ +\end_inset + + and +\begin_inset Formula $B$ +\end_inset + +. + The set of all pairs, denoted in mathematics by: +\begin_inset Formula +\[ +\left\{ (a,b)|a\in A,b\in B\right\} \quad, +\] \end_inset -Now compute -\begin_inset listings -inline true +is called the +\begin_inset Index idx status open \begin_layout Plain Layout - -f2(f1(h)) +Cartesian product \end_layout \end_inset - for arbitrary -\begin_inset listings -inline true -status open -\begin_layout Plain Layout +\series bold +Cartesian product +\series default + of sets +\begin_inset Formula $A$ +\end_inset -h: Unit => A -\end_layout + and +\begin_inset Formula $B$ +\end_inset +, and is denoted by +\begin_inset Formula $A\times B$ \end_inset - in Scala code: -\begin_inset listings -inline false +. + For this reason, the tuple type is also called the +\begin_inset Index idx status open \begin_layout Plain Layout +product type +\end_layout -f2(f1(h)) == f2(h(())) == { _ => h(()) } +\end_inset + + +\series bold +product type +\series default +. + Accordingly, the type notation adopts the symbol +\begin_inset Formula $\times$ +\end_inset + + for the product type. \end_layout +\begin_layout Standard +The set of all distinct values of the type +\begin_inset Formula $A+B$ \end_inset -How can we show that the function +, i.e., of the Scala type \begin_inset listings inline true status open \begin_layout Plain Layout -{_ => h(())} +Either[A, B] \end_layout \end_inset - is equal to -\begin_inset listings -inline true +, is a +\begin_inset Index idx status open \begin_layout Plain Layout - -h +labeled union \end_layout \end_inset -? Whenever we apply equal functions to equal arguments, they return equal - results. - In our case, the argument of +labeled union of the set of values of the form \begin_inset listings inline true status open \begin_layout Plain Layout -h +Left(a) \end_layout \end_inset - is of type + and the set of values of the form \begin_inset listings inline true status open \begin_layout Plain Layout -Unit +Right(b) \end_layout \end_inset -, so we only need to verify that the result of applying -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -h -\end_layout +. + It is clear that the cardinalities of these two sets are equal to +\begin_inset Formula $\left|A\right|$ +\end_inset + and +\begin_inset Formula $\left|B\right|$ \end_inset - to the value + respectively. + So, the cardinality of the type \begin_inset listings inline true status open \begin_layout Plain Layout -() +Either[A, B] \end_layout \end_inset - is the same as the result of applying -\begin_inset listings -inline true + is equal to +\begin_inset Formula $\left|A\right|+\left|B\right|$ +\end_inset + +. + For this reason, disjunctive types are also called +\begin_inset Index idx status open \begin_layout Plain Layout +sum type!see +\begin_inset Quotes eld +\end_inset + +disjunctive type +\begin_inset Quotes erd +\end_inset + -{_ => h(())} \end_layout \end_inset - to -\begin_inset listings -inline true -status open -\begin_layout Plain Layout +\series bold +sum types +\series default +, and the type notation adopts the symbol +\begin_inset Formula $+$ +\end_inset -() + for these types. \end_layout +\begin_layout Standard +We can write our conclusions as: +\begin_inset Formula +\begin{align*} +\left|A\times B\right| & =\left|A\right|\times\left|B\right|\quad,\\ +\left|A+B\right| & =\left|A\right|+\left|B\right|\quad. +\end{align*} + \end_inset -. - In other words, we need to apply both sides to an additional argument +The type notation, +\begin_inset Formula $A\times B$ +\end_inset + + for \begin_inset listings inline true status open \begin_layout Plain Layout -() +(A,B) \end_layout \end_inset -: + and +\begin_inset Formula $A+B$ +\end_inset + + for \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -f2(f1(h))(()) == { _ => h(()) } (()) == h(()) +Either[A, B] \end_layout \end_inset -This completes the proof. +, translates directly into type cardinalities. \end_layout \begin_layout Standard -For comparison, let us show the same proof in the code notation. - The functions -\begin_inset Formula $f_{1}$ +The last step is to notice that two types can be equivalent, +\begin_inset Formula $P\cong Q$ \end_inset - and -\begin_inset Formula $f_{2}$ +, only if their cardinalities are equal, +\begin_inset Formula $\left|P\right|=\left|Q\right|$ \end_inset - are: -\begin_inset Formula -\[ -f_{1}\triangleq h^{:\bbnum 1\rightarrow A}\rightarrow h(1)\quad,\quad\quad f_{2}\triangleq x^{:A}\rightarrow1\rightarrow x\quad. -\] - +. + When the cardinalities are not equal, +\begin_inset Formula $\left|P\right|\neq\left|Q\right|$ \end_inset -Now write the function compositions in both directions: -\begin_inset Formula -\begin{align*} -\text{expect to equal }\text{id}:\quad & f_{1}\bef f_{2}=(h^{:\bbnum 1\rightarrow A}\rightarrow h(1))\bef(x^{:A}\rightarrow1\rightarrow x)\\ -\text{compute composition}:\quad & \quad=h^{:\bbnum 1\rightarrow A}\rightarrow\gunderline{1\rightarrow h(1)}\\ -\text{note that }1\rightarrow h(1)\text{ is the same as }h:\quad & \quad=(h^{:\bbnum 1\rightarrow A}\rightarrow h)=\text{id}\quad. -\end{align*} - +, it will be impossible to have a one-to-one correspondence between the + sets of values of type +\begin_inset Formula $P$ \end_inset - -\begin_inset Formula -\begin{align*} -\text{expect to equal }\text{id}:\quad & f_{2}\bef f_{1}=(x^{:A}\rightarrow1\rightarrow x)\bef(h^{:\bbnum 1\rightarrow A}\rightarrow h(1))\\ -\text{compute composition}:\quad & \quad=x^{:A}\rightarrow\gunderline{(1\rightarrow x)(1)}\\ -\text{apply function}:\quad & \quad=(x^{:A}\rightarrow x)=\text{id}\quad. -\end{align*} - + and values of type +\begin_inset Formula $Q$ \end_inset - -\end_layout - -\begin_layout Standard -The type -\begin_inset Formula $\bbnum 1\rightarrow A$ +. + So, it will be impossible to convert values from type +\begin_inset Formula $P$ \end_inset - is equivalent to the type -\begin_inset Formula $A$ + to type +\begin_inset Formula $Q$ \end_inset - in the sense of carrying the same information, but these types are not - exactly the same. - An important difference between these types is that a value of type -\begin_inset Formula $A$ -\end_inset + and back without loss of information. +\end_layout - is available immediately, while a value of type -\begin_inset Formula $\bbnum 1\rightarrow A$ -\end_inset +\begin_layout Standard +We conclude that types are equivalent when a logical identity +\emph on +and +\emph default + an arithmetic identity hold. +\end_layout - is a function that still needs to be applied to an argument (of type -\begin_inset Formula $\bbnum 1$ -\end_inset +\begin_layout Standard +The presence of both identities does not automatically guarantee a useful + type equivalence. + The fact that information in one type can be identically stored in another + type does not necessarily mean that it is helpful to do so in a given applicati +on. +\end_layout -) before a value of type -\begin_inset Formula $A$ -\end_inset +\begin_layout Standard +For example, the types +\begin_inset listings +inline true +status open - is obtained. - The type -\begin_inset Formula $\bbnum 1\rightarrow A$ -\end_inset +\begin_layout Plain Layout - may represent an -\begin_inset Quotes eld -\end_inset +Option[Option[A]] +\end_layout -on-call -\begin_inset Quotes erd \end_inset - -\begin_inset Index idx + and +\begin_inset listings +inline true status open \begin_layout Plain Layout -on-call value -\end_layout -\end_inset +Either[Boolean, A] +\end_layout - value of type -\begin_inset Formula $A$ \end_inset -. - That value is computed on demand every time it is requested. - (See Section -\begin_inset space ~ + are equivalent because both types contain +\begin_inset Formula $2+\left|A\right|$ \end_inset - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Lazy-values-iterators-and-streams" -plural "false" -caps "false" -noprefix "false" - + distinct values. + The short notation for these types is +\begin_inset Formula $\bbnum 1+\bbnum 1+A$ \end_inset - for more details about -\begin_inset Quotes eld + and +\begin_inset Formula $\bbnum 2+A$ \end_inset -on-call -\begin_inset Quotes erd + respectively. + The type Boolean is denoted by +\begin_inset Formula $\bbnum 2$ \end_inset - values.) + since it has only +\emph on +two +\emph default + distinct values. + \end_layout \begin_layout Standard -The void type -\begin_inset Index idx +One could write code for converting between these types without loss of + information: +\begin_inset listings +inline false status open \begin_layout Plain Layout -void type + +def f1[A]: Option[Option[A]] => Either[Boolean, A] = { \end_layout -\end_inset +\begin_layout Plain Layout - -\begin_inset Formula $\bbnum 0$ -\end_inset + case None => Left(false) // Or maybe Left(true)? +\end_layout - needs special reasoning, as the next examples show: +\begin_layout Plain Layout + + case Some(None) => Left(true) \end_layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Example-type-identity-0-to-A" +\begin_layout Plain Layout -\end_inset + case Some(Some(x)) => Right(x) +\end_layout +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Example-type-identity-0-to-A" -plural "false" -caps "false" -noprefix "false" +} +\end_layout -\end_inset +\begin_layout Plain Layout + +\end_layout +\begin_layout Plain Layout +def f2[A]: Either[Boolean, A] => Option[Option[A]] = { \end_layout -\begin_layout Standard -Verify the type equivalence -\begin_inset Formula $\bbnum 0\rightarrow A\cong\bbnum 1$ -\end_inset +\begin_layout Plain Layout -. + case Left(false) => None \end_layout -\begin_layout Subparagraph -Solution +\begin_layout Plain Layout + + case Left(true) => Some(None) +\end_layout + +\begin_layout Plain Layout + + case Right(x) => Some(Some(x)) +\end_layout + +\begin_layout Plain Layout + +} \end_layout -\begin_layout Standard -To verify that a type -\begin_inset Formula $X$ \end_inset - is equivalent to the +The presence of an arbitrary choice in this code is a warning sign. + In \begin_inset listings inline true status open \begin_layout Plain Layout -Unit +f1 \end_layout \end_inset - type, we need to show that there is only one distinct value of type -\begin_inset Formula $X$ -\end_inset +, we could map +\begin_inset listings +inline true +status open -. - So, let us find out how many values the type -\begin_inset Formula $\bbnum 0\rightarrow A$ -\end_inset +\begin_layout Plain Layout - has. - Consider a value of that type, which is a function -\begin_inset Formula $f^{:\bbnum 0\rightarrow A}$ -\end_inset +None +\end_layout - from the type -\begin_inset Formula $\bbnum 0$ \end_inset - to a type -\begin_inset Formula $A$ -\end_inset + to +\begin_inset listings +inline true +status open -. - Since there exist no values of type -\begin_inset Formula $\bbnum 0$ -\end_inset +\begin_layout Plain Layout -, the function -\begin_inset Formula $f$ -\end_inset +Left(false) +\end_layout - will never be applied to any arguments and so -\emph on -does not need -\emph default - to compute any actual values of type -\begin_inset Formula $A$ \end_inset -. - So, -\begin_inset Formula $f$ -\end_inset + or to +\begin_inset listings +inline true +status open - is a function whose body may be -\begin_inset Quotes eld -\end_inset +\begin_layout Plain Layout + +Left(true) +\end_layout -empty -\begin_inset Quotes erd \end_inset -. - At least, -\begin_inset Formula $f$ + and adjust the rest of the code accordingly. + The type equivalence holds with either choice. + So, these types +\emph on +are +\emph default + equivalent, but there is no +\begin_inset Quotes eld \end_inset -'s body does not need to contain any expressions of type -\begin_inset Formula $A$ +natural +\begin_inset Quotes erd \end_inset -. - In Scala, such a function can be written as: + choice of the conversion functions \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def absurd[A]: Nothing => A = { ??? } +f1 \end_layout \end_inset -This code will compile without type errors. - An equivalent code is: + and \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def absurd[A]: Nothing => A = { x => ??? } +f2 \end_layout \end_inset -The symbol -\begin_inset listings -inline true + that will work correctly in all applications, because the meaning of these + data types will be application-dependent. + We call this type equivalence +\series bold +accidental +\series default + +\begin_inset Index idx status open \begin_layout Plain Layout +type equivalence!accidental +\end_layout -??? +\end_inset + +. \end_layout +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Example-cardinality-option-either" + \end_inset - is defined in the Scala library and represents code that is -\begin_inset Quotes eld + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Example-cardinality-option-either" +plural "false" +caps "false" +noprefix "false" + \end_inset -not implemented -\begin_inset Quotes erd + +\begin_inset Index idx +status open + +\begin_layout Plain Layout +examples (with code) +\end_layout + \end_inset -. - Trying to evaluate this symbol will produce an error: + +\end_layout + +\begin_layout Standard +Are the types \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -scala> val x = ??? +Option[A] \end_layout +\end_inset + + and +\begin_inset listings +inline true +status open + \begin_layout Plain Layout -scala.NotImplementedError: an implementation is missing +Either[Unit, A] \end_layout \end_inset -Since the function + equivalent? Check whether the corresponding logic identity and arithmetic + identity hold. +\end_layout + +\begin_layout Paragraph +Solution +\end_layout + +\begin_layout Standard +Begin by writing the given types in the type notation: \begin_inset listings inline true status open \begin_layout Plain Layout -absurd +Option[A] \end_layout \end_inset - can never be applied to an argument, this error will never happen. - So, one can pretend that the result value (which will never be computed) - has any required type, e.g., the type -\begin_inset Formula $A$ + is written as +\begin_inset Formula $\bbnum 1+A$ \end_inset -. - In this way, the compiler will accept the definition of +, and \begin_inset listings inline true status open \begin_layout Plain Layout -absurd +Either[Unit, A] \end_layout +\end_inset + + is written also as +\begin_inset Formula $\bbnum 1+A$ \end_inset . + The notation already indicates that the types are equivalent. + But let us verify explicitly that the type notation is not misleading us + here. \end_layout \begin_layout Standard -Let us now verify that there exists -\emph on -only one -\emph default - distinct function of type -\begin_inset Formula $\bbnum 0\rightarrow A$ +To establish the type equivalence, we need to implement two fully parametric + functions: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def f1[A]: Option[A] => Either[Unit, A] = ??? +\end_layout + +\begin_layout Plain Layout + +def f2[A]: Either[Unit, A] => Option[A] = ??? +\end_layout + \end_inset -. - Take any two functions of that type, -\begin_inset Formula $f^{:\bbnum 0\rightarrow A}$ +These functions must satisfy +\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ \end_inset and -\begin_inset Formula $g^{:\bbnum 0\rightarrow A}$ +\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ \end_inset . - Are they different? The only way of showing that -\begin_inset Formula $f$ + It is straightforward to implement +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +f1 +\end_layout + \end_inset and -\begin_inset Formula $g$ -\end_inset +\begin_inset listings +inline true +status open - are different is by finding a value -\begin_inset Formula $x$ -\end_inset +\begin_layout Plain Layout - such that -\begin_inset Formula $f(x)\neq g(x)$ -\end_inset +f2 +\end_layout -. - But then -\begin_inset Formula $x$ \end_inset - would be of type -\begin_inset Formula $\bbnum 0$ -\end_inset +: +\begin_inset listings +inline false +status open -, and there are -\emph on -no -\emph default - -\emph on -values -\emph default - of type -\begin_inset Formula $\bbnum 0$ -\end_inset +\begin_layout Plain Layout -. - So, we will never be able to find the required value -\begin_inset Formula $x$ -\end_inset +def f1[A]: Option[A] => Either[Unit, A] = { +\end_layout -. - It follows that any two functions -\begin_inset Formula $f$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $g$ -\end_inset + case None => Left(()) +\end_layout - of type -\begin_inset Formula $\bbnum 0\rightarrow A$ -\end_inset +\begin_layout Plain Layout - are equal, -\begin_inset Formula $f=g$ -\end_inset + case Some(x) => Right(x) +\end_layout -. - In other words, there exists only one distinct value of type -\begin_inset Formula $\bbnum 0\rightarrow A$ -\end_inset +\begin_layout Plain Layout -. - Since the cardinality of the type -\begin_inset Formula $\bbnum 0\rightarrow A$ -\end_inset +} +\end_layout - is -\begin_inset Formula $1$ -\end_inset +\begin_layout Plain Layout -, we obtain the type equivalence -\begin_inset Formula $\bbnum 0\rightarrow A\cong\bbnum 1$ -\end_inset +def f2[A]: Either[Unit, A] => Option[A] = { +\end_layout -. +\begin_layout Plain Layout + + case Left(()) => None \end_layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Example-type-identity-A-0" +\begin_layout Plain Layout -\end_inset + case Right(x) => Some(x) +\end_layout +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Example-type-identity-A-0" -plural "false" -caps "false" -noprefix "false" +} +\end_layout \end_inset +The code clearly shows that +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +f1 \end_layout -\begin_layout Standard -Show that -\begin_inset Formula $A\rightarrow\bbnum 0\not\cong\bbnum 0$ \end_inset and -\begin_inset Formula $A\rightarrow\bbnum 0\not\cong\bbnum 1$ -\end_inset +\begin_inset listings +inline true +status open -, where -\begin_inset Formula $A$ -\end_inset +\begin_layout Plain Layout - is an arbitrary type. +f2 \end_layout -\begin_layout Subparagraph -Solution +\end_inset + + are inverses of each other. + This verifies the type equivalence. \end_layout \begin_layout Standard -To prove that two types are -\emph on -not -\emph default - equivalent, it is sufficient to show that their cardinalities are different. - Let us determine the cardinality of the type -\begin_inset Formula $A\rightarrow\bbnum 0$ +The logic identity is +\begin_inset Formula $True\vee A=True\vee A$ \end_inset -, assuming that the cardinality of -\begin_inset Formula $A$ -\end_inset + and holds trivially. + It remains to check the arithmetic identity, which relates the cardinalities + of types +\begin_inset listings +inline true +status open - is known. - We note that a function of type, say, -\begin_inset Formula $\text{Int}\rightarrow\bbnum 0$ -\end_inset +\begin_layout Plain Layout - is impossible to implement. - (If we had such a function -\begin_inset Formula $f^{:\text{Int}\rightarrow\bbnum 0}$ -\end_inset +Option[A] +\end_layout -, we could evaluate, say, -\begin_inset Formula $x\triangleq f(123)$ \end_inset - and obtain a value -\begin_inset Formula $x$ -\end_inset + and +\begin_inset listings +inline true +status open - of type -\begin_inset Formula $\bbnum 0$ -\end_inset +\begin_layout Plain Layout -, which is impossible by definition of the type -\begin_inset Formula $\bbnum 0$ -\end_inset +Either[Unit, A] +\end_layout -. - It follows that -\begin_inset Formula $\left|\text{Int}\rightarrow\bbnum 0\right|=0$ \end_inset . - However, Example -\begin_inset space ~ -\end_inset - + Assume that the cardinality of type +\begin_inset listings +inline true +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Example-type-identity-0-to-A" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout -\end_inset +A +\end_layout - shows that -\begin_inset Formula $\bbnum 0\rightarrow\bbnum 0$ \end_inset - has cardinality -\begin_inset Formula $1$ + is +\begin_inset Formula $\left|A\right|$ \end_inset . - So, we find that -\begin_inset Formula $\left|A\rightarrow\bbnum 0\right|=1$ -\end_inset + Any possible value of type +\begin_inset listings +inline true +status open - if the type -\begin_inset Formula $A$ -\end_inset +\begin_layout Plain Layout - is itself -\begin_inset Formula $\bbnum 0$ -\end_inset +Option[A] +\end_layout - but -\begin_inset Formula $\left|A\rightarrow\bbnum 0\right|=0$ \end_inset - for all other types -\begin_inset Formula $A$ -\end_inset + must be either +\begin_inset listings +inline true +status open -. - We conclude that the type -\begin_inset Formula $A\rightarrow\bbnum 0$ -\end_inset +\begin_layout Plain Layout + +None +\end_layout - is not equivalent to -\begin_inset Formula $\bbnum 0$ \end_inset or -\begin_inset Formula $\bbnum 1$ -\end_inset +\begin_inset listings +inline true +status open - when -\begin_inset Formula $A$ -\end_inset +\begin_layout Plain Layout - is an unknown type. - The type -\begin_inset Formula $A\rightarrow\bbnum 0$ -\end_inset +Some(x) +\end_layout - is void for non-void types -\begin_inset Formula $A$ \end_inset -, and vice versa. -\end_layout +, where +\begin_inset listings +inline true +status open -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Example-type-identity-2" +\begin_layout Plain Layout + +x +\end_layout \end_inset + is a value of type +\begin_inset listings +inline true +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Example-type-identity-2" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout + +A +\end_layout \end_inset +. + So, the number of distinct values of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +Option[A] \end_layout -\begin_layout Standard -Verify the type equivalence -\begin_inset Formula $A\rightarrow\bbnum 1\cong\bbnum 1$ +\end_inset + + is +\begin_inset Formula $1+\left|A\right|$ \end_inset . -\end_layout + All possible values of type +\begin_inset listings +inline true +status open -\begin_layout Subparagraph -Solution +\begin_layout Plain Layout + +Either[Unit, A] \end_layout -\begin_layout Standard -There is only one fully parametric function that returns -\begin_inset Formula $\bbnum 1$ \end_inset -: + are of the form \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f[A]: A => Unit = { _ => () } +Left(()) \end_layout \end_inset -The function -\begin_inset Formula $f$ -\end_inset + or +\begin_inset listings +inline true +status open - cannot use its argument of type -\begin_inset Formula $A$ -\end_inset +\begin_layout Plain Layout - since nothing is known about the type -\begin_inset Formula $A$ -\end_inset +Right(x) +\end_layout -. - So, the code of -\begin_inset Formula $f$ \end_inset - -\emph on -must -\emph default - discard its argument and return the fixed value +, where \begin_inset listings inline true status open \begin_layout Plain Layout -() +x \end_layout \end_inset - of type + is a value of type \begin_inset listings inline true status open \begin_layout Plain Layout -Unit +A \end_layout \end_inset . - In the code notation, this function is written as: -\begin_inset Formula -\[ -f^{:A\rightarrow\bbnum 1}\triangleq\_\rightarrow1\quad. -\] + So, the cardinality of type +\begin_inset listings +inline true +status open -\end_inset +\begin_layout Plain Layout -We can show that there exists only -\emph on -one -\emph default - distinct function of type -\begin_inset Formula $A\rightarrow\bbnum 1$ -\end_inset +Either[Unit, A] +\end_layout - (that is, the type -\begin_inset Formula $A\rightarrow\bbnum 1$ \end_inset - has cardinality -\begin_inset Formula $1$ + is +\begin_inset Formula $1+\left|A\right|$ \end_inset -). - Assume that -\begin_inset Formula $f$ +. + We see that the arithmetic identity holds: the types +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Option[A] +\end_layout + \end_inset and -\begin_inset Formula $g$ -\end_inset +\begin_inset listings +inline true +status open - are two such functions, and try to find a value -\begin_inset Formula $x^{:A}$ -\end_inset +\begin_layout Plain Layout - such that -\begin_inset Formula $f(x)\neq g(x)$ -\end_inset +Either[Unit, A] +\end_layout -. - We cannot find any such -\begin_inset Formula $x$ \end_inset - because -\begin_inset Formula $f(x)=1$ -\end_inset + have equally many distinct values. +\end_layout - and -\begin_inset Formula $g(x)=1$ +\begin_layout Standard +This example shows that the type notation is helpful for reasoning about + type equivalences. + The answer was found immediately when we wrote the type notation ( +\begin_inset Formula $\bbnum 1+A$ \end_inset - for all -\begin_inset Formula $x$ -\end_inset +) for the given types. +\end_layout -. - So, any two functions -\begin_inset Formula $f$ -\end_inset +\begin_layout Subsection +Type equivalence involving function types +\end_layout - and -\begin_inset Formula $g$ +\begin_layout Standard +Until now, we have looked at product types and disjunctive types. + We now turn to type constructions involving function types. +\end_layout + +\begin_layout Standard +Consider two types +\begin_inset Formula $A$ \end_inset - of type -\begin_inset Formula $A\rightarrow\bbnum 1$ + and +\begin_inset Formula $B$ \end_inset - must be equal to each other. - The cardinality of the type -\begin_inset Formula $A\rightarrow\bbnum 1$ + having known cardinalities +\begin_inset Formula $\left|A\right|$ \end_inset - is -\begin_inset Formula $1$ + and +\begin_inset Formula $\left|B\right|$ \end_inset . -\end_layout - -\begin_layout Standard -Any type having cardinality -\begin_inset Formula $1$ + How many distinct values does the function type +\begin_inset Formula $A\rightarrow B$ \end_inset - is equivalent to the + have? A function \begin_inset listings inline true status open \begin_layout Plain Layout -Unit +f: A => B \end_layout \end_inset - type ( -\begin_inset Formula $\bbnum 1$ + needs to select a value of type +\begin_inset Formula $B$ \end_inset -). - So, -\begin_inset Formula $A\rightarrow\bbnum 1\cong\bbnum 1$ + for each possible value of type +\begin_inset Formula $A$ \end_inset . -\end_layout - -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Example-type-identity-6-1" - -\end_inset + Therefore, the number of different functions +\begin_inset listings +inline true +status open +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Example-type-identity-6-1" -plural "false" -caps "false" -noprefix "false" +f: A => B +\end_layout \end_inset + is +\begin_inset Formula $\left|B\right|^{\left|A\right|}$ +\end_inset +. + \end_layout \begin_layout Standard -Denote by -\begin_inset Formula $\_^{:A}\rightarrow B$ +Here +\begin_inset Formula $\left|B\right|^{\left|A\right|}$ \end_inset - the type of + denotes the +\series bold +numeric exponent +\series default + \begin_inset Index idx status open \begin_layout Plain Layout -constant function +exponent \end_layout \end_inset -constant functions of type -\begin_inset Formula $A\rightarrow B$ -\end_inset - - (functions that ignore their argument). - Show that the type -\begin_inset Formula $\_^{:A}\rightarrow B$ -\end_inset - - is equivalent to the type -\begin_inset Formula $B$ +, that is, +\begin_inset Formula $\left|B\right|$ \end_inset -, as long as -\begin_inset Formula $A\neq\bbnum 0$ + to the power +\begin_inset Formula $\left|A\right|$ \end_inset . -\end_layout + We use the numeric exponent notation ( +\begin_inset Formula $a^{b}$ +\end_inset -\begin_layout Subparagraph -Solution +) only when computing with numbers. + When denoting types and code, this book uses superscripts for type parameters + and type annotations. \end_layout \begin_layout Standard -An isomorphism between the types -\begin_inset Formula $B$ +For the types +\begin_inset Formula $A=B=\text{Int}$ \end_inset - and -\begin_inset Formula $\_^{:A}\rightarrow B$ +, we have +\begin_inset Formula $\left|A\right|=\left|B\right|=2^{32}$ \end_inset - is given by the two functions: +, and the exponential formula gives: \begin_inset Formula -\begin{align*} - & f_{1}:B\rightarrow\_^{:A}\rightarrow B\quad,\quad\quad f_{1}\triangleq b\rightarrow\_\rightarrow b\quad;\\ - & f_{2}:(\_^{:A}\rightarrow B)\rightarrow B\quad,\quad\quad f_{2}\triangleq k^{:\_\rightarrow B}\rightarrow k(x^{:A})\quad, -\end{align*} +\[ +\left|A\rightarrow B\right|=(2^{32})^{\left(2^{32}\right)}=2^{32\times2^{32}}=2^{2^{37}}\approx10^{4.1\times10^{10}}\quad. +\] \end_inset -where -\begin_inset Formula $x$ -\end_inset +This number greatly exceeds the number of atoms in the observable Universe. +\begin_inset Foot +status open - is any value of type -\begin_inset Formula $A$ -\end_inset +\begin_layout Plain Layout +Estimated in +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "https://www.universetoday.com/36302/atoms-in-the-universe/amp/" +literal "false" -. - That value exists since the type -\begin_inset Formula $A$ \end_inset - is not void. - The function -\begin_inset Formula $f_{2}$ + +\family default + to be between +\begin_inset Formula $10^{78}$ \end_inset - does not depend on the choice of -\begin_inset Formula $x$ + and +\begin_inset Formula $10^{82}$ \end_inset - because -\begin_inset Formula $k$ +. +\end_layout + \end_inset - is a constant function, so -\begin_inset Formula $k(x)$ + However, almost all of those functions will map integers to integers in + extremely complicated (and practically useless) ways. + The code of those functions will be much larger than the available memory + of a realistic computer. + So, the number of practically implementable functions of type +\begin_inset Formula $A\rightarrow B$ \end_inset - is the same for all -\begin_inset Formula $x$ + can be much smaller than +\begin_inset Formula $\left|B\right|^{\left|A\right|}$ \end_inset . - In other words, the function -\begin_inset Formula $k$ + Since the code of a function is a string of bytes that needs to fit into + the computer's memory, the number of implementable functions is no larger + than the number of possible byte strings. +\end_layout + +\begin_layout Standard +Nevertheless, the formula +\begin_inset Formula $\left|B\right|^{\left|A\right|}$ \end_inset - satisfies -\begin_inset Formula $k=(\_\rightarrow k(x))$ + is useful since it shows the number of distinct functions that are possible + in principle. + When types +\begin_inset Formula $A$ \end_inset - with any chosen -\begin_inset Formula $x$ + and +\begin_inset Formula $B$ \end_inset -. - To prove that -\begin_inset Formula $f_{1}$ + have only a small number of distinct values (for example, +\begin_inset Formula $A=$ +\end_inset + + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Option[Boolean]] +\end_layout + \end_inset and -\begin_inset Formula $f_{2}$ +\begin_inset Formula $B=$ \end_inset - are inverses: -\begin_inset Formula -\begin{align*} - & f_{1}\bef f_{2}=(b\rightarrow\_\rightarrow b)\bef(k\rightarrow k(x))=b\rightarrow(\_\rightarrow b)(x)=(b\rightarrow b)=\text{id}\quad,\\ - & f_{2}\bef f_{1}=(k\rightarrow k(x))\bef(b\rightarrow\_\rightarrow b)=k\rightarrow\_\rightarrow k(x)=k\rightarrow k=\text{id}\quad. -\end{align*} + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Either[Boolean, Boolean] +\end_layout \end_inset +), the formula +\begin_inset Formula $\left|B\right|^{\left|A\right|}$ +\end_inset + gives an exact and practically relevant answer. \end_layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Example-type-identity-5" - +\begin_layout Standard +Let us now look for logic identities and arithmetic identities involving + function types. + Table +\begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-Example-type-identity-5" +reference "tab:Logical-identities-with-function-types" plural "false" caps "false" noprefix "false" \end_inset + lists the available identities and the corresponding type equivalences. + (In the last column, we defined +\begin_inset Formula $a\triangleq\left|A\right|$ +\end_inset + +, +\begin_inset Formula $b\triangleq\left|B\right|$ +\end_inset + +, and +\begin_inset Formula $c\triangleq\left|C\right|$ +\end_inset + for brevity.) \end_layout \begin_layout Standard -Verify the following type equivalence: -\begin_inset Formula -\[ -A+B\rightarrow C\cong(A\rightarrow C)\times(B\rightarrow C)\quad. -\] +It is notable that no logic identity is available for the formula +\begin_inset Formula $\alpha\Rightarrow\left(\beta\vee\gamma\right)$ +\end_inset +, and correspondingly no type equivalence is available for the type expression + +\begin_inset Formula $A\rightarrow B+C$ \end_inset + (although there is an identity for +\begin_inset Formula $A\rightarrow B\times C$ +\end_inset -\end_layout +). + Reasoning about types of the form +\begin_inset Formula $A\rightarrow B+C$ +\end_inset -\begin_layout Subparagraph -Solution + is more complicated because those types usually cannot be transformed into + simpler types. \end_layout \begin_layout Standard -Begin by implementing two functions with type signatures: -\begin_inset listings -inline false +\begin_inset Float table +wide false +sideways false status open \begin_layout Plain Layout - -def f1[A,B,C]: (Either[A, B] => C) => (A => C, B => C) = ??? -\end_layout +\align center +\begin_inset Tabular + + + + + + + +\begin_inset Text \begin_layout Plain Layout -def f2[A,B,C]: ((A => C, B => C)) => Either[A, B] => C = ??? +\series bold +\size footnotesize +Logical identity (if holds) \end_layout \end_inset - -The code can be derived unambiguously from the type signatures. - For the first function, we need to produce a pair of functions of type - -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -(A => C, B => C) +\series bold +\size footnotesize +Type equivalence \end_layout \end_inset - -. - Can we produce the first part of that pair? Computing a function of type - -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -A => C -\end_layout +\series bold +\size footnotesize +Arithmetic identity +\end_layout \end_inset - - means that we need to produce a value of type -\begin_inset listings -inline true -status open + + + + +\begin_inset Text \begin_layout Plain Layout -C -\end_layout - +\size footnotesize +\begin_inset Formula $\left(True\Rightarrow\alpha\right)=\alpha$ \end_inset - given an arbitrary value -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -a:A \end_layout \end_inset - -. - The available data is a function of type -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -Either[A, B] => C -\end_layout - +\size footnotesize +\begin_inset Formula $\bbnum 1\rightarrow A\cong A$ \end_inset - called, say, -\begin_inset listings -inline true -status open -\begin_layout Plain Layout - -h \end_layout \end_inset - -. - We can apply that function to -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -Left(a) +\size footnotesize +\begin_inset Formula $a^{1}=a$ +\end_inset + + \end_layout \end_inset - - and obtain a value of type -\begin_inset listings -inline true -status open + + + + +\begin_inset Text \begin_layout Plain Layout -C +\size footnotesize +\begin_inset Formula $\left(False\Rightarrow\alpha\right)=True$ +\end_inset + + \end_layout \end_inset - - as required. - So, a function of type -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -A => C +\size footnotesize +\begin_inset Formula $\bbnum 0\rightarrow A\cong\bbnum 1$ +\end_inset + + \end_layout \end_inset - - is computed as -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -a => h(Left(a)) +\size footnotesize +\begin_inset Formula $a^{0}=1$ +\end_inset + + \end_layout \end_inset - -. - We can produce a function of type -\begin_inset listings -inline true -status open + + + + +\begin_inset Text \begin_layout Plain Layout -B => C +\size footnotesize +\begin_inset Formula $\left(\alpha\Rightarrow True\right)=True$ +\end_inset + + \end_layout \end_inset - - similarly. - The code is: -\begin_inset listings -inline false -status open + + +\begin_inset Text \begin_layout Plain Layout -def f1[A,B,C]: (Either[A, B] => C) => (A => C, B => C) = -\end_layout +\size footnotesize +\begin_inset Formula $A\rightarrow\bbnum 1\cong\bbnum 1$ +\end_inset -\begin_layout Plain Layout - (h: Either[A, B] => C) => (a => h(Left(a)), b => h(Right(b))) \end_layout \end_inset + + +\begin_inset Text -We write this function in the code notation like this: -\begin_inset Formula -\begin{align*} - & f_{1}:\left(A+B\rightarrow C\right)\rightarrow\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)\quad,\\ - & f_{1}\triangleq h^{:A+B\rightarrow C}\rightarrow\big(a^{:A}\rightarrow h(a+\bbnum 0^{:B})\big)\times\big(b^{:B}\rightarrow h(\bbnum 0^{:A}+b)\big)\quad. -\end{align*} +\begin_layout Plain Layout +\size footnotesize +\begin_inset Formula $1^{a}=1$ \end_inset \end_layout -\begin_layout Standard -For the function -\begin_inset listings -inline true -status open +\end_inset + + + + +\begin_inset Text \begin_layout Plain Layout -f2 +\size footnotesize +\begin_inset Formula $\left(\alpha\Rightarrow False\right)\neq False$ +\end_inset + + \end_layout \end_inset - -, we need to apply pattern matching to both curried arguments and then return - a value of type -\begin_inset listings -inline true -status open + + +\begin_inset Text \begin_layout Plain Layout -C +\size footnotesize +\begin_inset Formula $A\rightarrow\bbnum 0\not\cong\bbnum 0$ +\end_inset + + \end_layout \end_inset - -. - This can be achieved in only one way: -\begin_inset listings -inline false -status open + + +\begin_inset Text \begin_layout Plain Layout -def f2[A,B,C](f: A => C, g: B => C): Either[A, B] => C = { -\end_layout +\size footnotesize +\begin_inset Formula $0^{a}\neq0$ +\end_inset -\begin_layout Plain Layout - case Left(a) => f(a) \end_layout +\end_inset + + + + +\begin_inset Text + \begin_layout Plain Layout - case Right(b) => g(b) -\end_layout +\size footnotesize +\begin_inset Formula $\left(\alpha\vee\beta\right)\Rightarrow\gamma=\left(\alpha\Rightarrow\gamma\right)\wedge\left(\beta\Rightarrow\gamma\right)$ +\end_inset -\begin_layout Plain Layout -} \end_layout \end_inset + + +\begin_inset Text -We write this function in the code notation like this: -\begin_inset Formula -\begin{align*} - & f_{2}:\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)\rightarrow A+B\rightarrow C\quad,\\ - & f_{2}\triangleq f^{:A\rightarrow C}\times g^{:B\rightarrow C}\rightarrow\,\begin{array}{|c||c|} - & C\\ -\hline A & a\rightarrow f(a)\\ -B & b\rightarrow g(b) -\end{array}\quad. -\end{align*} +\begin_layout Plain Layout +\size footnotesize +\begin_inset Formula $A+B\rightarrow C\cong\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)$ \end_inset -The matrix in the last line has only one column because the result type, - -\begin_inset Formula $C$ -\end_inset -, is not known to be a disjunctive type. - We may also simplify the functions, e.g., replace -\begin_inset Formula $a\rightarrow f(a)$ -\end_inset +\end_layout - by just -\begin_inset Formula $f$ \end_inset + + +\begin_inset Text -, and write: -\begin_inset Formula -\[ -f_{2}\triangleq f^{:A\rightarrow C}\times g^{:B\rightarrow C}\rightarrow\,\begin{array}{|c||c|} - & C\\ -\hline A & f\\ -B & g -\end{array}\quad. -\] +\begin_layout Plain Layout +\size footnotesize +\begin_inset Formula $c^{a+b}=c^{a}\times c^{b}$ \end_inset \end_layout -\begin_layout Standard -It remains to verify that -\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ \end_inset + + + + +\begin_inset Text - and -\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ +\begin_layout Plain Layout + +\size footnotesize +\begin_inset Formula $(\alpha\wedge\beta)\Rightarrow\gamma=\alpha\Rightarrow\left(\beta\Rightarrow\gamma\right)$ \end_inset -. - To compute -\begin_inset Formula $f_{1}\bef f_{2}$ + +\end_layout + \end_inset + + +\begin_inset Text -, we write (omitting types): -\begin_inset Formula -\begin{align*} -f_{1}\bef f_{2} & =\big(h\rightarrow(a\rightarrow h(a+\bbnum 0))\times(b\rightarrow h(\bbnum 0+b))\big)\bef\bigg(f\times g\rightarrow\,\begin{array}{||c|} -f\\ -g -\end{array}\,\bigg)\\ -\text{compute composition}:\quad & =h\rightarrow\,\begin{array}{||c|} -a\rightarrow h(a+\bbnum 0)\\ -b\rightarrow h(\bbnum 0+b) -\end{array}\quad. -\end{align*} +\begin_layout Plain Layout +\size footnotesize +\begin_inset Formula $A\times B\rightarrow C\cong A\rightarrow B\rightarrow C$ \end_inset -To proceed, we need to simplify the expressions -\begin_inset Formula $h(a+\bbnum 0)$ + +\end_layout + \end_inset + + +\begin_inset Text - and -\begin_inset Formula $h(\bbnum 0+b)$ +\begin_layout Plain Layout + +\size footnotesize +\begin_inset Formula $c^{a\times b}=(c^{b})^{a}$ \end_inset -. - We rewrite the argument -\begin_inset Formula $h$ + +\end_layout + \end_inset + + + + +\begin_inset Text - (an arbitrary function of type -\begin_inset Formula $A+B\rightarrow C$ +\begin_layout Plain Layout + +\size footnotesize +\begin_inset Formula $\alpha\Rightarrow\left(\beta\wedge\gamma\right)=\left(\alpha\Rightarrow\beta\right)\wedge\left(\alpha\Rightarrow\gamma\right)$ \end_inset -) in the matrix notation: -\begin_inset Formula -\[ -h\triangleq\,\begin{array}{|c||c|} - & C\\ -\hline A & a\rightarrow p(a)\\ -B & b\rightarrow q(b) -\end{array}\,=\,\begin{array}{|c||c|} - & C\\ -\hline A & p\\ -B & q -\end{array}\quad, -\] + +\end_layout \end_inset + + +\begin_inset Text -where -\begin_inset Formula $p^{:A\rightarrow C}$ +\begin_layout Plain Layout + +\size footnotesize +\begin_inset Formula $A\rightarrow B\times C\cong\left(A\rightarrow B\right)\times\left(A\rightarrow C\right)$ \end_inset - and -\begin_inset Formula $q^{:B\rightarrow C}$ + +\end_layout + \end_inset + + +\begin_inset Text - are new arbitrary functions. - Since we already checked the types, we can omit all type annotations and - write -\begin_inset Formula $h$ +\begin_layout Plain Layout + +\size footnotesize +\begin_inset Formula $\left(b\times c\right)^{a}=b^{a}\times c^{a}$ \end_inset - as: -\begin_inset Formula -\[ -h\triangleq\,\begin{array}{||c|} -p\\ -q -\end{array}\quad. -\] + +\end_layout \end_inset + + + -To evaluate expressions such as -\begin_inset Formula $h(a+\bbnum 0)$ \end_inset - and -\begin_inset Formula $h(\bbnum 0+b)$ + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +Logical identities with implication, and the corresponding type equivalences + and arithmetic identities. +\begin_inset CommandInset label +LatexCommand label +name "tab:Logical-identities-with-function-types" + \end_inset -, we need to use one of the rows of this matrix. - The correct row will be selected -\emph on -automatically -\emph default - by the rules of matrix multiplication if we place a row vector to the left - of the matrix and use the convention of omitting terms containing -\begin_inset Formula $\bbnum 0$ + +\end_layout + \end_inset -: -\begin_inset Formula -\[ -\begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array}\,=a\triangleright p\quad,\quad\quad\begin{array}{|cc|} -\bbnum 0 & b\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array}\,=b\triangleright q\quad. -\] + +\end_layout \end_inset -Here we used the symbol -\begin_inset Formula $\triangleright$ + +\end_layout + +\begin_layout Standard +We will now prove some of the type identities in Table +\begin_inset space ~ \end_inset - to separate an argument from a function when the argument is written to - the -\emph on -left -\emph default - of the function. - The symbol -\begin_inset Formula $\triangleright$ + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Logical-identities-with-function-types" +plural "false" +caps "false" +noprefix "false" + \end_inset - (pronounced -\begin_inset Quotes eld +. +\end_layout + +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Example-type-identity-f" + \end_inset -pipe -\begin_inset Quotes erd + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Example-type-identity-f" +plural "false" +caps "false" +noprefix "false" + \end_inset @@ -20187,1208 +20674,922 @@ pipe status open \begin_layout Plain Layout -pipe notation +examples (with code) \end_layout \end_inset -\begin_inset Index idx -status open +\end_layout -\begin_layout Plain Layout -\begin_inset Formula $\triangleright$ +\begin_layout Standard +Verify the type equivalence +\begin_inset Formula $\bbnum 1\rightarrow A\cong A$ \end_inset --notation!see -\begin_inset Quotes eld -\end_inset +. +\end_layout -pipe notation -\begin_inset Quotes erd +\begin_layout Subparagraph +Solution +\end_layout + +\begin_layout Standard +Recall that the type notation +\begin_inset Formula $\bbnum 1\rightarrow A$ \end_inset + means the Scala function type +\begin_inset listings +inline true +status open +\begin_layout Plain Layout + +Unit => A \end_layout \end_inset -) is defined by -\begin_inset Formula $x\triangleright f\triangleq f(x)$ +. + There is only one value of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Unit +\end_layout + \end_inset . - In Scala, this operation is available as + The choice of a function of type \begin_inset listings inline true status open \begin_layout Plain Layout -x.pipe(f) +Unit => A \end_layout \end_inset - as of Scala 2.13. -\end_layout - -\begin_layout Standard -We can write values of disjunctive types, such as -\begin_inset Formula $a+\bbnum 0$ -\end_inset + is the same as the choice of a value of type +\begin_inset listings +inline true +status open -, as row vectors -\begin_inset Formula $\,\begin{array}{|cc|} -a & \bbnum 0\end{array}\,$ -\end_inset +\begin_layout Plain Layout -: -\begin_inset Formula -\begin{equation} -h(a+\bbnum 0)=(a+\bbnum 0)\triangleright h=\,\begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright\,h\quad.\label{eq:forward-notation-} -\end{equation} +A +\end_layout \end_inset -With these notations, we compute further. - Omit all terms applying -\begin_inset Formula $\bbnum 0$ +. + So, the type +\begin_inset Formula $\bbnum 1\rightarrow A$ \end_inset - or applying something to -\begin_inset Formula $\bbnum 0$ + has +\begin_inset Formula $\left|A\right|$ \end_inset -: -\begin_inset Formula -\begin{align*} - & \begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright\,h=\,\begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array}\,=a\triangleright p=p(a)\quad,\\ - & \begin{array}{|cc|} -\bbnum 0 & b\end{array}\,\triangleright\,h=\,\,\begin{array}{|cc|} -\bbnum 0 & b\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array}\,=b\triangleright q=q(b)\quad. -\end{align*} - -\end_inset + distinct values, and the arithmetic identity holds. +\end_layout -Now we can complete the proof of -\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ -\end_inset +\begin_layout Standard +To verify the type equivalence explicitly, we need to implement two functions: +\begin_inset listings +inline false +status open -: -\begin_inset Formula -\begin{align*} -f_{1}\bef f_{2} & =h\rightarrow\,\begin{array}{||c|} -a\rightarrow h(a+\bbnum 0)\\ -b\rightarrow h(\bbnum 0+b) -\end{array}\\ -\text{previous equations}:\quad & =\,\begin{array}{||c|} -p\\ -q -\end{array}\,\rightarrow\,\begin{array}{||c|} -a\rightarrow p(a)\\ -b\rightarrow q(b) -\end{array}\\ -\text{simplify functions}:\quad & =\,\,\begin{array}{||c|} -p\\ -q -\end{array}\,\rightarrow\,\begin{array}{||c|} -p\\ -q -\end{array}\,=\text{id}\quad. -\end{align*} +\begin_layout Plain Layout -\end_inset +def f1[A]: (Unit => A) => A = ??? +\end_layout +\begin_layout Plain Layout +def f2[A]: A => Unit => A = ??? \end_layout -\begin_layout Standard -To prove that -\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ -\end_inset - -, use the notation -\begin_inset space ~ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:forward-notation-" -plural "false" -caps "false" -noprefix "false" +The first function needs to produce a value of type +\begin_inset listings +inline true +status open -\end_inset +\begin_layout Plain Layout -): -\begin_inset Formula -\begin{align*} - & f_{2}\bef f_{1}=\bigg(f\times g\rightarrow\,\begin{array}{||c|} -f\\ -g -\end{array}\,\bigg)\bef\big(h\rightarrow(a\rightarrow(a+\bbnum 0)\triangleright h)\times(b\rightarrow(\bbnum 0+b)\triangleright h)\big)\\ -\text{compute composition}:\quad & =f\times g\rightarrow\big(a\rightarrow\gunderline{\,\begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright}\,\begin{array}{||c|} -f\\ -g -\end{array}\,\big)\times\big(b\rightarrow\gunderline{\,\begin{array}{|cc|} -\bbnum 0 & b\end{array}\,\triangleright}\,\begin{array}{||c|} -f\\ -g -\end{array}\,\big)\\ -\text{apply functions}:\quad & =f\times g\rightarrow(a\rightarrow\gunderline{a\triangleright f})\times(b\rightarrow\gunderline{b\triangleright g})\\ -\text{definition of }\triangleright:\quad & =f\times g\rightarrow\gunderline{\left(a\rightarrow f(a)\right)}\times\gunderline{\left(b\rightarrow g(b)\right)}\\ -\text{simplify functions}:\quad & =\left(f\times g\rightarrow f\times g\right)=\text{id}\quad. -\end{align*} +A +\end_layout \end_inset +, given an argument of the function type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +Unit => A \end_layout -\begin_layout Standard -In this way, we have proved that -\begin_inset Formula $f_{1}$ \end_inset - and -\begin_inset Formula $f_{2}$ -\end_inset +. + The only possibility is to apply that function to the value of type +\begin_inset listings +inline true +status open - are mutual inverses. - The proofs appear long because we took time to motivate and introduce new - notation for applying matrices to row vectors. - Once this notation is understood, the proof for -\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ -\end_inset +\begin_layout Plain Layout - can be written as: -\begin_inset Formula -\begin{align*} -f_{1}\bef f_{2} & =\left(h\rightarrow(a\rightarrow(a+\bbnum 0)\triangleright h)\times(b\rightarrow(\bbnum 0+b)\triangleright h)\right)\bef\bigg(f\times g\rightarrow\,\begin{array}{||c|} -f\\ -g -\end{array}\bigg)\\ -\text{compute composition}:\quad & =h\rightarrow\,\begin{array}{||c|} -\,a\,\rightarrow\,\left|\begin{array}{cc} -a & \bbnum 0\end{array}\right|\triangleright h\\ -b\rightarrow\left|\begin{array}{cc} -\bbnum 0 & b\end{array}\right|\triangleright h -\end{array}\,=\,\begin{array}{||c|} -p\\ -q -\end{array}\rightarrow\,\begin{array}{||c|} -a\rightarrow\,\begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array}\\ -b\,\rightarrow\,\begin{array}{|cc|} -\bbnum 0 & b\,\,\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array} -\end{array}\\ -\text{apply functions}:\quad & =\,\begin{array}{||c|} -p\\ -q -\end{array}\,\rightarrow\,\begin{array}{||c|} -a\rightarrow a\triangleright p\\ -b\rightarrow b\triangleright q -\end{array}\,=\,\begin{array}{||c|} -p\\ -q -\end{array}\,\rightarrow\,\begin{array}{||c|} -p\\ -q -\end{array}\,=\text{id}\quad. -\end{align*} +Unit +\end_layout \end_inset -Proofs in the code notation are shorter than in Scala syntax because certain - names and keywords (such as +. + We can always produce that value as \begin_inset listings inline true status open \begin_layout Plain Layout -Left +() \end_layout \end_inset -, +: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -Right +def f1[A]: (Unit => A) => A = (h: Unit => A) => h(()) \end_layout \end_inset -, +Implementing \begin_inset listings inline true status open \begin_layout Plain Layout -case +f2 \end_layout \end_inset -, + is straightforward. + We can just discard the \begin_inset listings inline true status open \begin_layout Plain Layout -match +Unit \end_layout \end_inset -, etc.) are omitted. - From now on, we will prefer to use the code notation in proofs, keeping - in mind that one can always convert the code notation to Scala. + argument: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def f2[A]: A => Unit => A = (x: A) => _ => x \end_layout -\begin_layout Standard -Note that the function arrow ( -\begin_inset Formula $\rightarrow$ \end_inset -) binds weaker than the pipe operation ( -\begin_inset Formula $\triangleright$ -\end_inset +It remains to show that the functions +\begin_inset listings +inline true +status open -), so the code notation -\begin_inset Formula $x\rightarrow y\triangleright z$ -\end_inset +\begin_layout Plain Layout - means -\begin_inset Formula $x\rightarrow(y\triangleright z)$ -\end_inset +f1 +\end_layout -. - We will review the code notation more systematically in Chapter -\begin_inset space ~ \end_inset + and +\begin_inset listings +inline true +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "chap:Reasoning-about-code" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout + +f2 +\end_layout \end_inset -. + are inverses of each other. + Let us show the proof using Scala code and then using the code notation. \end_layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Example-type-identity-6" +\begin_layout Standard +Writing Scala code, compute +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +f1(f2(x)) +\end_layout \end_inset + for an arbitrary +\begin_inset listings +inline true +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Example-type-identity-6" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout + +x:A +\end_layout \end_inset +. + Using the code of +\begin_inset listings +inline true +status open -\end_layout +\begin_layout Plain Layout -\begin_layout Standard -Verify the type equivalence: -\begin_inset Formula -\[ -A\times B\rightarrow C\cong A\rightarrow B\rightarrow C\quad. -\] +f1 +\end_layout \end_inset + and +\begin_inset listings +inline true +status open -\end_layout +\begin_layout Plain Layout -\begin_layout Subparagraph -Solution +f2 \end_layout -\begin_layout Standard -Begin by implementing the two functions: +\end_inset + +, we get: \begin_inset listings inline false status open \begin_layout Plain Layout -def f1[A,B,C]: (((A, B)) => C) => A => B => C = ??? +f1(f2(x)) == f1(_ => x) == (_ => x)(()) == x \end_layout +\end_inset + +Now compute +\begin_inset listings +inline true +status open + \begin_layout Plain Layout -def f2[A,B,C]: (A => B => C) => ((A, B)) => C = ??? +f2(f1(h)) \end_layout \end_inset -The Scala code can be derived from the type signatures unambiguously: + for arbitrary \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f1[A,B,C]: (((A, B)) => C) => A => B => C = g => a => b => g((a, b)) +h: Unit => A \end_layout +\end_inset + + in Scala code: +\begin_inset listings +inline false +status open + \begin_layout Plain Layout -def f2[A,B,C]: (A => B => C) => ((A, B)) => C = h => { case (a, b) => h(a)(b) - } +f2(f1(h)) == f2(h(())) == { _ => h(()) } \end_layout \end_inset -Write these functions in the code notation: -\begin_inset Formula -\begin{align*} - & f_{1}=g^{:A\times B\rightarrow C}\rightarrow a^{:A}\rightarrow b^{:B}\rightarrow g(a\times b)\quad,\\ - & f_{2}=h^{:A\rightarrow B\rightarrow C}\rightarrow\left(a\times b\right)^{:A\times B}\rightarrow h(a)(b)\quad. -\end{align*} +How can we show that the function +\begin_inset listings +inline true +status open -\end_inset +\begin_layout Plain Layout + +{_ => h(())} +\end_layout -We denote by -\begin_inset Formula $\left(a\times b\right)^{:A\times B}$ \end_inset - the argument of type + is equal to \begin_inset listings inline true status open \begin_layout Plain Layout -(A, B) +h \end_layout \end_inset - with pattern matching implied. - This notation allows us to write shorter code formulas involving tupled - arguments. -\end_layout +? Whenever we apply equal functions to equal arguments, they return equal + results. + In our case, the argument of +\begin_inset listings +inline true +status open -\begin_layout Standard -Compute the function composition -\begin_inset Formula $f_{1}\bef f_{2}$ -\end_inset +\begin_layout Plain Layout - and show that it is equal to an identity function: -\begin_inset Formula -\begin{align*} -\text{expect to equal }\text{id}:\quad & f_{1}\bef f_{2}=(g\rightarrow\gunderline{a\rightarrow b\rightarrow g(a\times b)})\bef\left(h\rightarrow a\times b\rightarrow h(a)(b)\right)\\ -\text{substitute }h=a\rightarrow b\rightarrow g(a\times b):\quad & \quad=g\rightarrow\gunderline{a\times b\rightarrow g(a\times b)}\\ -\text{simplify function}:\quad & \quad=\left(g\rightarrow g\right)=\text{id}\quad. -\end{align*} +h +\end_layout \end_inset -Compute the function composition -\begin_inset Formula $f_{2}\bef f_{1}$ -\end_inset + is of type +\begin_inset listings +inline true +status open - and show that it is equal to an identity function: -\begin_inset Formula -\begin{align*} -\text{expect to equal }\text{id}:\quad & f_{2}\bef f_{1}=(h\rightarrow\gunderline{a\times b\rightarrow h(a)(b)})\bef\left(g\rightarrow a\rightarrow b\rightarrow g(a\times b)\right)\\ -\text{substitute }g=a\times b\rightarrow h(a)(b):\quad & \quad=h\rightarrow a\rightarrow\gunderline{b\rightarrow h(a)(b)}\\ -\text{simplify function }b\rightarrow h(a)(b):\quad & \quad=h\rightarrow\gunderline{a\rightarrow h(a)}\\ -\text{simplify function }a\rightarrow h(a)\text{ to }h:\quad & \quad=\left(h\rightarrow h\right)=\text{id}\quad. -\end{align*} +\begin_layout Plain Layout + +Unit +\end_layout \end_inset +, so we only need to verify that the result of applying +\begin_inset listings +inline true +status open -\end_layout +\begin_layout Plain Layout -\begin_layout Section -Summary +h \end_layout -\begin_layout Standard -What tasks can we perform now? -\end_layout +\end_inset -\begin_layout Itemize -Convert a fully parametric type signature into a logical formula and: -\end_layout + to the value +\begin_inset listings +inline true +status open -\begin_deeper -\begin_layout Itemize -Decide whether the type signature can be implemented in code. -\end_layout +\begin_layout Plain Layout -\begin_layout Itemize -If possible, derive the code using the CH correspondence. +() \end_layout -\end_deeper -\begin_layout Itemize -Use the type notation (Table -\begin_inset space ~ \end_inset + is the same as the result of applying +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:ch-correspondence-type-notation-CH-propositions" -plural "false" -caps "false" -noprefix "false" +{_ => h(())} +\end_layout \end_inset -) for reasoning about types to: + to +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +() \end_layout -\begin_deeper -\begin_layout Itemize -Decide type equivalence using the rules in Tables -\begin_inset space ~ \end_inset +. + In other words, we need to apply both sides to an additional argument +\begin_inset listings +inline true +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Logical-identities-with-disjunction-and-conjunction" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout + +() +\end_layout \end_inset -– -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Logical-identities-with-function-types" -plural "false" -caps "false" -noprefix "false" +: +\begin_inset listings +inline false +status open -\end_inset +\begin_layout Plain Layout -. +f2(f1(h))(()) == { _ => h(()) } (()) == h(()) \end_layout -\begin_layout Itemize -Simplify type expressions before writing code. -\end_layout +\end_inset -\end_deeper -\begin_layout Itemize -Use the matrix notation and the pipe notation to write code that works on - disjunctive types. +This completes the proof. \end_layout \begin_layout Standard -What tasks -\emph on -cannot -\emph default - be performed with these tools? -\end_layout +For comparison, let us show the same proof in the code notation. + The functions +\begin_inset Formula $f_{1}$ +\end_inset -\begin_layout Itemize -Automatically generate code for -\emph on -recursive -\emph default - functions. - The CH correspondence is based on propositional logic, which cannot describe - recursion. - Accordingly, recursion is absent from the eight code constructions of Section -\begin_inset space ~ + and +\begin_inset Formula $f_{2}$ \end_inset + are: +\begin_inset Formula +\[ +f_{1}\triangleq h^{:\bbnum 1\rightarrow A}\rightarrow h(1)\quad,\quad\quad f_{2}\triangleq x^{:A}\rightarrow1\rightarrow x\quad. +\] -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:The-rules-of-proof" -plural "false" -caps "false" -noprefix "false" +\end_inset + +Now write the function compositions in both directions: +\begin_inset Formula +\begin{align*} +\text{expect to equal }\text{id}:\quad & f_{1}\bef f_{2}=(h^{:\bbnum 1\rightarrow A}\rightarrow h(1))\bef(x^{:A}\rightarrow1\rightarrow x)\\ +\text{compute composition}:\quad & \quad=h^{:\bbnum 1\rightarrow A}\rightarrow\gunderline{1\rightarrow h(1)}\\ +\text{note that }1\rightarrow h(1)\text{ is the same as }h:\quad & \quad=(h^{:\bbnum 1\rightarrow A}\rightarrow h)=\text{id}\quad. +\end{align*} + +\end_inset + + +\begin_inset Formula +\begin{align*} +\text{expect to equal }\text{id}:\quad & f_{2}\bef f_{1}=(x^{:A}\rightarrow1\rightarrow x)\bef(h^{:\bbnum 1\rightarrow A}\rightarrow h(1))\\ +\text{compute composition}:\quad & \quad=x^{:A}\rightarrow\gunderline{(1\rightarrow x)(1)}\\ +\text{apply function}:\quad & \quad=(x^{:A}\rightarrow x)=\text{id}\quad. +\end{align*} \end_inset -. - Recursive functions need to be coded by hand. -\end_layout -\begin_layout Itemize -Automatically generate code satisfying a property (e.g., isomorphism). - We may generate some code, but the CH correspondence does not guarantee - that properties will hold. - We need to verify the required properties manually, after deriving the - code. \end_layout -\begin_layout Itemize -Express complicated conditions (e.g., +\begin_layout Standard +The type +\begin_inset Formula $\bbnum 1\rightarrow A$ +\end_inset + + is equivalent to the type +\begin_inset Formula $A$ +\end_inset + + in the sense of carrying the same information, but these types are not + exactly the same. + An important difference between these types is that a value of type +\begin_inset Formula $A$ +\end_inset + + is available immediately, while a value of type +\begin_inset Formula $\bbnum 1\rightarrow A$ +\end_inset + + is a function that still needs to be applied to an argument (of type +\begin_inset Formula $\bbnum 1$ +\end_inset + +) before a value of type +\begin_inset Formula $A$ +\end_inset + + is obtained. + The type +\begin_inset Formula $\bbnum 1\rightarrow A$ +\end_inset + + may represent an \begin_inset Quotes eld \end_inset -array is sorted +on-call \begin_inset Quotes erd \end_inset -) in a type signature. - This can be done using -\series bold -dependent types -\series default \begin_inset Index idx status open \begin_layout Plain Layout -dependent type +on-call value \end_layout \end_inset - (i.e., types that directly depend on values in some way). - This is an advanced technique beyond the scope of this book. - Programming languages such as Coq, Agda, and Idris fully support dependent - types, while Scala has only limited support. -\end_layout + value of type +\begin_inset Formula $A$ +\end_inset -\begin_layout Itemize -Generate code using type constructors with known methods (e.g., the -\begin_inset listings -inline true -status open +. + That value is computed on demand every time it is requested. + (See Section +\begin_inset space ~ +\end_inset -\begin_layout Plain Layout -map -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Lazy-values-iterators-and-streams" +plural "false" +caps "false" +noprefix "false" \end_inset - method). + for more details about +\begin_inset Quotes eld +\end_inset + +on-call +\begin_inset Quotes erd +\end_inset + + values.) \end_layout \begin_layout Standard -As an example of using type constructors with known methods, consider this - type signature: -\begin_inset listings -inline false +The void type +\begin_inset Index idx status open \begin_layout Plain Layout - -def q[A]: Array[A] => (A => Option[B]) => Array[Option[B]] +void type \end_layout \end_inset -Can we generate the code of this function from its type signature? We know - that the Scala library defines a -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout + +\begin_inset Formula $\bbnum 0$ +\end_inset -map + needs special reasoning, as the next examples show: \end_layout -\end_inset +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Example-type-identity-0-to-A" - method on the -\begin_inset listings -inline true -status open +\end_inset -\begin_layout Plain Layout -Array -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Example-type-identity-0-to-A" +plural "false" +caps "false" +noprefix "false" \end_inset - class. - So, an implementation of -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -q \end_layout +\begin_layout Standard +Verify the type equivalence +\begin_inset Formula $\bbnum 0\rightarrow A\cong\bbnum 1$ \end_inset - is: -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout +. +\end_layout -def q[A]: Array[A] => (A => Option[B]) => Array[Option[B]] = { arr => f - => arr.map(f) } +\begin_layout Subparagraph +Solution \end_layout +\begin_layout Standard +To verify that a type +\begin_inset Formula $X$ \end_inset -However, it is hard to create an -\emph on -algorithm -\emph default - that can derive this implementation automatically from the type signature - of + is equivalent to the \begin_inset listings inline true status open \begin_layout Plain Layout -q +Unit \end_layout \end_inset - via the Curry-Howard correspondence. - The algorithm would have to convert the type signature of -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout + type, we need to show that there is only one distinct value of type +\begin_inset Formula $X$ +\end_inset -q -\end_layout +. + So, let us find out how many values the type +\begin_inset Formula $\bbnum 0\rightarrow A$ +\end_inset + has. + Consider a value of that type, which is a function +\begin_inset Formula $f^{:\bbnum 0\rightarrow A}$ \end_inset - into this logical formula: -\begin_inset Formula -\begin{equation} -{\cal CH}(\text{Array}^{A})\Rightarrow{\cal CH}(A\rightarrow\text{Opt}^{B})\Rightarrow{\cal CH}(\text{Array}^{\text{Opt}^{B}})\quad.\label{eq:ch-example-quantified-proposition} -\end{equation} + from the type +\begin_inset Formula $\bbnum 0$ +\end_inset + to a type +\begin_inset Formula $A$ \end_inset -To derive an implementation, the algorithm would need to use the available - -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -map -\end_layout +. + Since there exist no values of type +\begin_inset Formula $\bbnum 0$ +\end_inset +, the function +\begin_inset Formula $f$ \end_inset - method for -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Array -\end_layout - + will never be applied to any arguments and so +\emph on +does not need +\emph default + to compute any actual values of type +\begin_inset Formula $A$ \end_inset . - That method has the type signature: -\begin_inset Formula -\[ -\text{map}:\forall(A,B).\,\text{Array}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Array}^{B}\quad. -\] - -\end_inset - -To derive the -\begin_inset Formula ${\cal CH}$ -\end_inset - --proposition -\begin_inset space ~ -\end_inset - -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-quantified-proposition" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -), the algorithm will need to assume that the -\begin_inset Formula ${\cal CH}$ -\end_inset - --proposition: -\begin_inset Formula -\begin{equation} -{\cal CH}\,\big(\forall(A,B).\,\text{Array}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Array}^{B}\big)\label{eq:ch-example-quantified-proposition-2} -\end{equation} - + So, +\begin_inset Formula $f$ \end_inset -already holds. - In other words Eq. -\begin_inset space ~ + is a function whose body may be +\begin_inset Quotes eld \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-quantified-proposition-2" -plural "false" -caps "false" -noprefix "false" - +empty +\begin_inset Quotes erd \end_inset -) is one of the premises of a sequent. - Reasoning about premises such as Eq. -\begin_inset space ~ +. + At least, +\begin_inset Formula $f$ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-quantified-proposition-2" -plural "false" -caps "false" -noprefix "false" - +'s body does not need to contain any expressions of type +\begin_inset Formula $A$ \end_inset -) requires -\begin_inset Index idx +. + In Scala, such a function can be written as: +\begin_inset listings +inline false status open \begin_layout Plain Layout -first-order logic -\end_layout - -\end_inset +def absurd[A]: Nothing => A = { ??? } +\end_layout -\series bold -first-order logic -\series default - — a logic whose proof rules can handle quantified types such as -\begin_inset Formula $\forall(A,B)$ \end_inset - -\emph on - -\emph default -inside premises. - However, first-order logic is -\series bold -undecidable -\series default - -\begin_inset Index idx +This code will compile without type errors. + An equivalent code is: +\begin_inset listings +inline false status open \begin_layout Plain Layout -undecidable logic -\end_layout - -\end_inset -: no algorithm can find a proof (or verify the absence of a proof) in all - cases. - +def absurd[A]: Nothing => A = { x => ??? } \end_layout -\begin_layout Standard -The constructive propositional logic with the rules listed in Table -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Proof-rules-for-constructive-logic" -plural "false" -caps "false" -noprefix "false" - \end_inset - is -\series bold -decidable -\series default -, -\begin_inset Index idx +The symbol +\begin_inset listings +inline true status open \begin_layout Plain Layout -decidable logic + +??? \end_layout \end_inset - i.e., it has an algorithm that either finds a proof or disproves any given - formula. - However, that logic cannot handle type constructors such as -\begin_inset Formula $\text{Array}^{A}$ + is defined in the Scala library and represents code that is +\begin_inset Quotes eld \end_inset -. - It also cannot handle premises containing type quantifiers such as -\begin_inset Formula $\forall(A,B)$ +not implemented +\begin_inset Quotes erd \end_inset -, because all the available rules have the quantifiers placed -\emph on -outside -\emph default - the premises. - -\end_layout - -\begin_layout Standard -So, code for functions such as +. + Trying to evaluate this symbol will produce an error: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -q -\end_layout - -\end_inset - - can only be derived by trial and error, informed by intuition. - This book will help programmers to acquire the necessary intuition and - technique. +scala> val x = ??? \end_layout -\begin_layout Subsection -Solved examples -\begin_inset Index idx -status open - \begin_layout Plain Layout -solved examples -\end_layout - -\end_inset - +scala.NotImplementedError: an implementation is missing \end_layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-solvedExample-1" - -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-solvedExample-1" -plural "false" -caps "false" -noprefix "false" - \end_inset - -\end_layout - -\begin_layout Standard -Find the cardinality of the type +Since the function \begin_inset listings inline true status open \begin_layout Plain Layout -P = Option[Option[Boolean] => Boolean] +absurd \end_layout +\end_inset + + can never be applied to an argument, this error will never happen. + So, one can pretend that the result value (which will never be computed) + has any required type, e.g., the type +\begin_inset Formula $A$ \end_inset . - Write + In this way, the compiler will accept the definition of \begin_inset listings inline true status open \begin_layout Plain Layout -P +absurd \end_layout \end_inset - in the type notation and simplify it to an equivalent type. -\end_layout - -\begin_layout Subparagraph -Solution +. \end_layout \begin_layout Standard -Begin with the type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Option[Boolean] -\end_layout - -\end_inset - -, which can be either -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -None -\end_layout - -\end_inset - - or -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Some(x) -\end_layout - -\end_inset - - with an -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -x:Boolean -\end_layout - +Let us now verify that there exists +\emph on +only one +\emph default + distinct function of type +\begin_inset Formula $\bbnum 0\rightarrow A$ \end_inset . - Since the type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Boolean -\end_layout - + Take any two functions of that type, +\begin_inset Formula $f^{:\bbnum 0\rightarrow A}$ \end_inset - has -\begin_inset Formula $2$ + and +\begin_inset Formula $g^{:\bbnum 0\rightarrow A}$ \end_inset - possible values, the type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Option[Boolean] -\end_layout - +. + Are they different? The only way of showing that +\begin_inset Formula $f$ \end_inset - has -\begin_inset Formula $3$ + and +\begin_inset Formula $g$ \end_inset - values: -\begin_inset Formula -\[ -|\text{Opt}^{\text{Boolean}}|=\left|\bbnum 1+\text{Boolean}\right|=1+\left|\text{Boolean}\right|=1+2=3\quad. -\] - + are different is by finding a value +\begin_inset Formula $x$ \end_inset -In the type notation, -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Boolean -\end_layout - + such that +\begin_inset Formula $f(x)\neq g(x)$ \end_inset - is denoted by the symbol -\begin_inset Formula $\bbnum 2$ +. + But then +\begin_inset Formula $x$ \end_inset -, and the type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Option[Boolean] -\end_layout - + would be of type +\begin_inset Formula $\bbnum 0$ \end_inset - by -\begin_inset Formula $\bbnum 1+\bbnum 2$ +, and there are +\emph on +no +\emph default + +\emph on +values +\emph default + of type +\begin_inset Formula $\bbnum 0$ \end_inset . - So, the type notation -\begin_inset Formula $\bbnum 1+\bbnum 2$ + So, we will never be able to find the required value +\begin_inset Formula $x$ \end_inset - is consistent with the cardinality -\begin_inset Formula $3$ +. + It follows that any two functions +\begin_inset Formula $f$ \end_inset - of that type: -\begin_inset Formula -\[ -\left|\bbnum 1+\text{Boolean}\right|=\left|\bbnum 1+\bbnum 2\right|=1+2=3\quad. -\] - + and +\begin_inset Formula $g$ \end_inset - -\end_layout - -\begin_layout Standard -The function type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Option[Boolean] => Boolean -\end_layout - + of type +\begin_inset Formula $\bbnum 0\rightarrow A$ \end_inset - is denoted by -\begin_inset Formula $\bbnum 1+\bbnum 2\rightarrow\bbnum 2$ + are equal, +\begin_inset Formula $f=g$ \end_inset . - Compute its cardinality as: -\begin_inset Formula -\[ -|\text{Opt}^{\text{Boolean}}\rightarrow\text{Boolean}|=\left|\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right|=\left|\bbnum 2\right|^{\left|\bbnum 1+\bbnum 2\right|}=2^{3}=8\quad. -\] - + In other words, there exists only one distinct value of type +\begin_inset Formula $\bbnum 0\rightarrow A$ \end_inset -Finally, the we write -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -P -\end_layout - +. + Since the cardinality of the type +\begin_inset Formula $\bbnum 0\rightarrow A$ \end_inset - in the type notation as -\begin_inset Formula $P=\bbnum 1+\left(\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right)$ + is +\begin_inset Formula $1$ \end_inset - and find: -\begin_inset Formula -\[ -\left|P\right|=\left|\bbnum 1+\left(\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right)\right|=1+\left|\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right|=1+8=9\quad. -\] - +, we obtain the type equivalence +\begin_inset Formula $\bbnum 0\rightarrow A\cong\bbnum 1$ \end_inset - +. \end_layout \begin_layout Subsubsection Example \begin_inset CommandInset label LatexCommand label -name "subsec:ch-solvedExample-2" +name "subsec:ch-Example-type-identity-A-0" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-solvedExample-2" +reference "subsec:ch-Example-type-identity-A-0" plural "false" caps "false" noprefix "false" @@ -21399,27 +21600,19 @@ noprefix "false" \end_layout \begin_layout Standard -Implement a Scala type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -P[A] -\end_layout - +Show that +\begin_inset Formula $A\rightarrow\bbnum 0\not\cong\bbnum 0$ \end_inset - given by this type notation: -\begin_inset Formula -\[ -P^{A}\triangleq1+A+\text{Int}\times A+(\text{String}\rightarrow A)\quad. -\] - + and +\begin_inset Formula $A\rightarrow\bbnum 0\not\cong\bbnum 1$ \end_inset +, where +\begin_inset Formula $A$ +\end_inset + is an arbitrary type. \end_layout \begin_layout Subparagraph @@ -21427,180 +21620,135 @@ Solution \end_layout \begin_layout Standard -To translate type notation into Scala code, begin by defining the disjunctive - types as case classes, choosing class names for convenience. - In this case, -\begin_inset Formula $P^{A}$ +To prove that two types are +\emph on +not +\emph default + equivalent, it is sufficient to show that their cardinalities are different. + Let us determine the cardinality of the type +\begin_inset Formula $A\rightarrow\bbnum 0$ \end_inset - is a disjunctive type with four parts, so we need four case classes: -\begin_inset listings -inline false -status open +, assuming that the cardinality of +\begin_inset Formula $A$ +\end_inset -\begin_layout Plain Layout + is known. + We note that a function of type, say, +\begin_inset Formula $\text{Int}\rightarrow\bbnum 0$ +\end_inset -sealed trait P[A] -\end_layout + is impossible to implement. + (If we had such a function +\begin_inset Formula $f^{:\text{Int}\rightarrow\bbnum 0}$ +\end_inset -\begin_layout Plain Layout +, we could evaluate, say, +\begin_inset Formula $x\triangleq f(123)$ +\end_inset -final case class P1[A](???) extends P[A] -\end_layout + and obtain a value +\begin_inset Formula $x$ +\end_inset -\begin_layout Plain Layout - -final case class P2[A](???) extends P[A] -\end_layout - -\begin_layout Plain Layout - -final case class P3[A](???) extends P[A] -\end_layout - -\begin_layout Plain Layout - -final case class P4[A](???) extends P[A] -\end_layout + of type +\begin_inset Formula $\bbnum 0$ +\end_inset +, which is impossible by definition of the type +\begin_inset Formula $\bbnum 0$ \end_inset -Each of the case classes represents one part of the disjunctive type. - Now we write the contents for each of the case classes, in order to implement - the data in each of the disjunctive parts: -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -sealed trait P[A] -\end_layout - -\begin_layout Plain Layout - -final case class P1[A]() extends P[A] -\end_layout - -\begin_layout Plain Layout - -final case class P2[A](x: A) extends P[A] -\end_layout - -\begin_layout Plain Layout - -final case class P3[A](n: Int, x: A) extends P[A] -\end_layout - -\begin_layout Plain Layout - -final case class P4[A](f: String => A) extends P[A] -\end_layout - +. + It follows that +\begin_inset Formula $\left|\text{Int}\rightarrow\bbnum 0\right|=0$ \end_inset - -\end_layout - -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-solvedExample-2a" - +. + However, Example +\begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-solvedExample-2a" +reference "subsec:ch-Example-type-identity-0-to-A" plural "false" caps "false" noprefix "false" \end_inset + shows that +\begin_inset Formula $\bbnum 0\rightarrow\bbnum 0$ +\end_inset -\end_layout - -\begin_layout Standard -Find an equivalent disjunctive type for the type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -P = (Either[A, B], Either[C, D]) -\end_layout - + has cardinality +\begin_inset Formula $1$ \end_inset . -\end_layout - -\begin_layout Subparagraph -Solution -\end_layout - -\begin_layout Standard -Begin by writing the given type in the type notation. - The tuple becomes the product type, and -\begin_inset listings -inline true -status open + So, we find that +\begin_inset Formula $\left|A\rightarrow\bbnum 0\right|=1$ +\end_inset -\begin_layout Plain Layout + if the type +\begin_inset Formula $A$ +\end_inset -Either -\end_layout + is itself +\begin_inset Formula $\bbnum 0$ +\end_inset + but +\begin_inset Formula $\left|A\rightarrow\bbnum 0\right|=0$ \end_inset - becomes the disjunctive (or -\begin_inset Quotes eld + for all other types +\begin_inset Formula $A$ \end_inset -sum -\begin_inset Quotes erd +. + We conclude that the type +\begin_inset Formula $A\rightarrow\bbnum 0$ \end_inset -) type: -\begin_inset Formula -\[ -P\triangleq(A+B)\times(C+D)\quad. -\] + is not equivalent to +\begin_inset Formula $\bbnum 0$ +\end_inset + or +\begin_inset Formula $\bbnum 1$ \end_inset -By the usual rules of arithmetic, we expand brackets and obtain an equivalent - type: -\begin_inset Formula -\[ -P\cong A\times C+A\times D+B\times C+B\times D\quad. -\] + when +\begin_inset Formula $A$ +\end_inset + is an unknown type. + The type +\begin_inset Formula $A\rightarrow\bbnum 0$ \end_inset -This is a disjunctive type having -\begin_inset Formula $4$ + is void for non-void types +\begin_inset Formula $A$ \end_inset - parts. +, and vice versa. \end_layout \begin_layout Subsubsection Example \begin_inset CommandInset label LatexCommand label -name "subsec:ch-solvedExample-3" +name "subsec:ch-Example-type-identity-2" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-solvedExample-3" +reference "subsec:ch-Example-type-identity-2" plural "false" caps "false" noprefix "false" @@ -21611,19 +21759,11 @@ noprefix "false" \end_layout \begin_layout Standard -Show that the following type equivalences do -\emph on -not -\emph default - hold: -\begin_inset Formula $A+A\not\cong A$ -\end_inset - - and -\begin_inset Formula $A\times A\not\cong A$ +Verify the type equivalence +\begin_inset Formula $A\rightarrow\bbnum 1\cong\bbnum 1$ \end_inset -, although the corresponding logical identities hold. +. \end_layout \begin_layout Subparagraph @@ -21631,374 +21771,319 @@ Solution \end_layout \begin_layout Standard -Note that the arithmetic equalities do not hold, -\begin_inset Formula $A+A\neq A$ -\end_inset - - and -\begin_inset Formula $A\times A\ne A$ -\end_inset - -. - This already indicates that the types are not equivalent. - To build further intuition, consider that a value of type -\begin_inset Formula $A+A$ +There is only one fully parametric function that returns +\begin_inset Formula $\bbnum 1$ \end_inset - (in Scala, +: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -Either[A, A] +def f[A]: A => Unit = { _ => () } \end_layout \end_inset -) is a -\begin_inset listings -inline true -status open +The function +\begin_inset Formula $f$ +\end_inset -\begin_layout Plain Layout + cannot use its argument of type +\begin_inset Formula $A$ +\end_inset -Left(a) -\end_layout + since nothing is known about the type +\begin_inset Formula $A$ +\end_inset +. + So, the code of +\begin_inset Formula $f$ \end_inset - or a + +\emph on +must +\emph default + discard its argument and return the fixed value \begin_inset listings inline true status open \begin_layout Plain Layout -Right(a) +() \end_layout \end_inset - for some + of type \begin_inset listings inline true status open \begin_layout Plain Layout -a:A +Unit \end_layout \end_inset . - In the code notation, it is either -\begin_inset Formula $a^{:A}+\bbnum 0$ -\end_inset + In the code notation, this function is written as: +\begin_inset Formula +\[ +f^{:A\rightarrow\bbnum 1}\triangleq\_\rightarrow1\quad. +\] - or -\begin_inset Formula $\bbnum 0+a^{:A}$ \end_inset -. - So, a value of type -\begin_inset Formula $A+A$ +We can show that there exists only +\emph on +one +\emph default + distinct function of type +\begin_inset Formula $A\rightarrow\bbnum 1$ \end_inset - contains a value of type -\begin_inset Formula $A$ + (that is, the type +\begin_inset Formula $A\rightarrow\bbnum 1$ \end_inset - with the additional information about whether it is the first or the second - part of the disjunctive type. - We cannot represent that information in a single value of type -\begin_inset Formula $A$ + has cardinality +\begin_inset Formula $1$ \end_inset -. - -\end_layout - -\begin_layout Standard -Similarly, a value of type -\begin_inset Formula $A\times A$ +). + Assume that +\begin_inset Formula $f$ \end_inset - contains two (possibly different) values of type -\begin_inset Formula $A$ + and +\begin_inset Formula $g$ \end_inset -, which cannot be represented by a single value of type -\begin_inset Formula $A$ + are two such functions, and try to find a value +\begin_inset Formula $x^{:A}$ \end_inset - without loss of information. -\end_layout + such that +\begin_inset Formula $f(x)\neq g(x)$ +\end_inset -\begin_layout Standard -However, the corresponding logical identities -\begin_inset Formula $\alpha\vee\alpha=\alpha$ +. + We cannot find any such +\begin_inset Formula $x$ +\end_inset + + because +\begin_inset Formula $f(x)=1$ \end_inset and -\begin_inset Formula $\alpha\wedge\alpha=\alpha$ +\begin_inset Formula $g(x)=1$ \end_inset - hold. - To see that, we could derive the four formulas: -\begin_inset Formula -\[ -\alpha\vee\alpha\Rightarrow\alpha\quad,\quad\quad\alpha\Rightarrow\alpha\vee\alpha\quad,\quad\quad\alpha\wedge\alpha\Rightarrow\alpha\quad,\quad\quad\alpha\Rightarrow\alpha\wedge\alpha\quad, -\] + for all +\begin_inset Formula $x$ +\end_inset +. + So, any two functions +\begin_inset Formula $f$ \end_inset -using the proof rules of Section -\begin_inset space ~ + and +\begin_inset Formula $g$ \end_inset + of type +\begin_inset Formula $A\rightarrow\bbnum 1$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:The-rules-of-proof" -plural "false" -caps "false" -noprefix "false" + must be equal to each other. + The cardinality of the type +\begin_inset Formula $A\rightarrow\bbnum 1$ +\end_inset + is +\begin_inset Formula $1$ \end_inset . - Alternatively, we may use the CH correspondence and show that the type - signatures: -\begin_inset Formula -\[ -\forall A.\,A+A\rightarrow A\quad,\quad\quad\forall A.\,A\rightarrow A+A\quad,\quad\quad\forall A.\,A\times A\rightarrow A\quad,\quad\quad\forall A.\,A\rightarrow A\times A\quad -\] +\end_layout +\begin_layout Standard +Any type having cardinality +\begin_inset Formula $1$ \end_inset -can be implemented via fully parametric functions. - For a programmer, it is easier to write code than to guess the correct - sequence of proof rules. - For the first pair of type signatures, we find: + is equivalent to the \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def f1[A]: Either[A, A] => A = { +Unit \end_layout -\begin_layout Plain Layout +\end_inset - case Left(a) => a // No other choice here. -\end_layout + type ( +\begin_inset Formula $\bbnum 1$ +\end_inset -\begin_layout Plain Layout +). + So, +\begin_inset Formula $A\rightarrow\bbnum 1\cong\bbnum 1$ +\end_inset - case Right(a) => a // No other choice here. +. \end_layout -\begin_layout Plain Layout +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Example-type-identity-6-1" -} -\end_layout +\end_inset -\begin_layout Plain Layout -def f2[A]: A => Either[A, A] = { a => Left(a) } // Can be also Right(a). -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Example-type-identity-6-1" +plural "false" +caps "false" +noprefix "false" \end_inset -The presence of an arbitrary choice, to return -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -Left(a) \end_layout +\begin_layout Standard +Denote by +\begin_inset Formula $\_^{:A}\rightarrow B$ \end_inset - or -\begin_inset listings -inline true + the type of +\begin_inset Index idx status open \begin_layout Plain Layout - -Right(a) +constant function \end_layout \end_inset -, is a warning sign showing that additional information is required to create - a value of type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Either[A, A] -\end_layout +constant functions of type +\begin_inset Formula $A\rightarrow B$ +\end_inset + (functions that ignore their argument). + Show that the type +\begin_inset Formula $\_^{:A}\rightarrow B$ \end_inset -. - This is precisely the information present in the type -\begin_inset Formula $A+A$ + is equivalent to the type +\begin_inset Formula $B$ \end_inset - but missing in the type -\begin_inset Formula $A$ +, as long as +\begin_inset Formula $A\neq\bbnum 0$ \end_inset . \end_layout +\begin_layout Subparagraph +Solution +\end_layout + \begin_layout Standard -The code notation for these functions is: -\begin_inset Formula -\[ -f_{1}\triangleq\,\begin{array}{|c||c|} - & A\\ -\hline A & a^{:A}\rightarrow a\\ -A & a^{:A}\rightarrow a -\end{array}\,=\,\begin{array}{|c||c|} - & A\\ -\hline A & \text{id}\\ -A & \text{id} -\end{array}\quad,\quad\quad f_{2}\triangleq a^{:A}\rightarrow a+\bbnum 0^{:A}=\,\begin{array}{|c||cc|} - & A & A\\ -\hline A & a^{:A}\rightarrow a & \bbnum 0 -\end{array}\,=\,\begin{array}{|c||cc|} - & A & A\\ -\hline A & \text{id} & \bbnum 0 -\end{array}\quad. -\] +An isomorphism between the types +\begin_inset Formula $B$ +\end_inset + and +\begin_inset Formula $\_^{:A}\rightarrow B$ \end_inset -The composition of these functions is not equal to identity: + is given by the two functions: \begin_inset Formula -\[ -f_{1}\bef f_{2}=\,\begin{array}{||c|} -\text{id}\\ -\text{id} -\end{array}\,\bef\,\begin{array}{||cc|} -\text{id} & \bbnum 0\end{array}\,=\,\begin{array}{||cc|} -\text{id} & \bbnum 0\\ -\text{id} & \bbnum 0 -\end{array}\,\quad,\quad\text{while we have}\quad\text{id}^{:A+A\rightarrow A+A}=\,\begin{array}{||cc|} -\text{id} & \bbnum 0\\ -\bbnum 0 & \text{id} -\end{array}\quad. -\] +\begin{align*} + & f_{1}:B\rightarrow\_^{:A}\rightarrow B\quad,\quad\quad f_{1}\triangleq b\rightarrow\_\rightarrow b\quad;\\ + & f_{2}:(\_^{:A}\rightarrow B)\rightarrow B\quad,\quad\quad f_{2}\triangleq k^{:\_\rightarrow B}\rightarrow k(x^{:A})\quad, +\end{align*} \end_inset +where +\begin_inset Formula $x$ +\end_inset -\end_layout + is any value of type +\begin_inset Formula $A$ +\end_inset -\begin_layout Standard -For the second pair of type signatures, the code is: -\begin_inset listings -inline false -status open +. + That value exists since the type +\begin_inset Formula $A$ +\end_inset -\begin_layout Plain Layout + is not void. + The function +\begin_inset Formula $f_{2}$ +\end_inset -def f1[A]: ((A, A)) => A = { case (a1, a2) => a1 } // Could be also `a2`. -\end_layout + does not depend on the choice of +\begin_inset Formula $x$ +\end_inset -\begin_layout Plain Layout + because +\begin_inset Formula $k$ +\end_inset -cef f2[A]: A => (A, A) = { a => (a, a) } // No other choice - here. -\end_layout + is a constant function, so +\begin_inset Formula $k(x)$ +\end_inset + is the same for all +\begin_inset Formula $x$ \end_inset -It is clear that the first function loses information when it returns -\begin_inset listings -inline true -status open +. + In other words, the function +\begin_inset Formula $k$ +\end_inset -\begin_layout Plain Layout + satisfies +\begin_inset Formula $k=(\_\rightarrow k(x))$ +\end_inset -a1 -\end_layout + with any chosen +\begin_inset Formula $x$ +\end_inset +. + To prove that +\begin_inset Formula $f_{1}$ \end_inset - and discards -\begin_inset listings -inline true -status open + and +\begin_inset Formula $f_{2}$ +\end_inset -\begin_layout Plain Layout - -a2 -\end_layout - -\end_inset - - (or vice versa). -\end_layout - -\begin_layout Standard -The code notation for the functions -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -f1 -\end_layout - -\end_inset - - and -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -f2 -\end_layout - -\end_inset - - is: -\begin_inset Formula -\[ -f_{1}\triangleq a_{1}^{:A}\times a_{2}^{:A}\rightarrow a_{1}=\pi_{1}^{:A\times A\rightarrow A}\quad,\quad\quad f_{2}\triangleq a^{:A}\rightarrow a\times a=\Delta^{:A\rightarrow A\times A}\quad. -\] - -\end_inset - -Computing the compositions of these functions, we find that -\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ -\end_inset - - while -\begin_inset Formula $f_{1}\bef f_{2}\ne\text{id}$ -\end_inset - -: + are inverses: \begin_inset Formula \begin{align*} -f_{1}\bef f_{2} & =\left(a_{1}\times a_{2}\rightarrow a_{1}\right)\bef\left(a\rightarrow a\times a\right)\\ - & =\left(a_{1}\times a_{2}\rightarrow a_{1}\times a_{1}\right)\neq\text{id}=\left(a_{1}\times a_{2}\rightarrow a_{1}\times a_{2}\right)\quad. + & f_{1}\bef f_{2}=(b\rightarrow\_\rightarrow b)\bef(k\rightarrow k(x))=b\rightarrow(\_\rightarrow b)(x)=(b\rightarrow b)=\text{id}\quad,\\ + & f_{2}\bef f_{1}=(k\rightarrow k(x))\bef(b\rightarrow\_\rightarrow b)=k\rightarrow\_\rightarrow k(x)=k\rightarrow k=\text{id}\quad. \end{align*} \end_inset @@ -22006,26 +22091,18 @@ f_{1}\bef f_{2} & =\left(a_{1}\times a_{2}\rightarrow a_{1}\right)\bef\left(a\ri \end_layout -\begin_layout Standard -We have implemented all four type signatures as fully parametric functions, - which shows that the corresponding logical formulas are all true (i.e., can - be derived using the proof rules). - However, the functions cannot be inverses of each other. - So, the type equivalences do not hold. -\end_layout - \begin_layout Subsubsection Example \begin_inset CommandInset label LatexCommand label -name "subsec:ch-solvedExample-4" +name "subsec:ch-Example-type-identity-5" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-solvedExample-4" +reference "subsec:ch-Example-type-identity-5" plural "false" caps "false" noprefix "false" @@ -22036,13 +22113,15 @@ noprefix "false" \end_layout \begin_layout Standard -Show that -\begin_inset Formula $\left(\left(A\wedge B\right)\Rightarrow C\right)\neq(A\Rightarrow C)\vee(B\Rightarrow C)$ +Verify the following type equivalence: +\begin_inset Formula +\[ +A+B\rightarrow C\cong(A\rightarrow C)\times(B\rightarrow C)\quad. +\] + \end_inset - in the constructive logic, but the equality holds in Boolean logic. - This is another example where the Boolean reasoning fails to give correct - answers about implementability of type signatures. + \end_layout \begin_layout Subparagraph @@ -22050,866 +22129,751 @@ Solution \end_layout \begin_layout Standard -Begin by rewriting the logical equality as two implications: -\begin_inset Formula -\[ -(A\wedge B\Rightarrow C)\Rightarrow(A\Rightarrow C)\vee(B\Rightarrow C)\quad\text{ and }\quad\left((A\Rightarrow C)\vee(B\Rightarrow C)\right)\Rightarrow\left(\left(A\wedge B\right)\Rightarrow C\right)\quad. -\] - -\end_inset - -It is sufficient to show that one of these implications is incorrect. - Rather than looking for a proof tree in the constructive logic (which would - be difficult, since we need to demonstrate that -\emph on -no -\emph default - proof exists), let us use the CH correspondence. - According to the CH correspondence, an equivalent task is to implement - fully parametric functions with the type signatures: -\begin_inset Formula -\[ -(A\times B\rightarrow C)\rightarrow(A\rightarrow C)+(B\rightarrow C)\quad\text{ and }\quad(A\rightarrow C)+(B\rightarrow C)\rightarrow A\times B\rightarrow C\quad. -\] - -\end_inset - -For the first type signature, the Scala code is: +Begin by implementing two functions with type signatures: \begin_inset listings inline false status open \begin_layout Plain Layout -def f1[A,B,C]: (((A, B)) => C) => Either[A => C, B => C] = { k => ??? } +def f1[A,B,C]: (Either[A, B] => C) => (A => C, B => C) = ??? \end_layout -\end_inset - -We are required to return either a -\begin_inset listings -inline true -status open - \begin_layout Plain Layout -Left(g) +def f2[A,B,C]: ((A => C, B => C)) => Either[A, B] => C = ??? \end_layout \end_inset - with +The code can be derived unambiguously from the type signatures. + For the first function, we need to produce a pair of functions of type + \begin_inset listings inline true status open \begin_layout Plain Layout -g: A => C +(A => C, B => C) \end_layout \end_inset -, or a +. + Can we produce the first part of that pair? Computing a function of type + \begin_inset listings inline true status open \begin_layout Plain Layout -Right(h) +A => C \end_layout \end_inset - with + means that we need to produce a value of type \begin_inset listings inline true status open \begin_layout Plain Layout -h: B => C +C \end_layout \end_inset -. - The only given data is a function + given an arbitrary value \begin_inset listings inline true status open \begin_layout Plain Layout -k +a:A \end_layout \end_inset - of type -\begin_inset Formula $A\times B\rightarrow C$ -\end_inset - -, so the decision of whether to return a +. + The available data is a function of type \begin_inset listings inline true status open \begin_layout Plain Layout -Left +Either[A, B] => C \end_layout \end_inset - or a + called, say, \begin_inset listings inline true status open \begin_layout Plain Layout -Right +h \end_layout \end_inset - must be hard-coded in the function +. + We can apply that function to \begin_inset listings inline true status open \begin_layout Plain Layout -f1 +Left(a) \end_layout \end_inset - independently of + and obtain a value of type \begin_inset listings inline true status open \begin_layout Plain Layout -k +C \end_layout \end_inset -. - Can we produce a function + as required. + So, a function of type \begin_inset listings inline true status open \begin_layout Plain Layout -g +A => C \end_layout \end_inset - of type + is computed as \begin_inset listings inline true status open \begin_layout Plain Layout -A => C +a => h(Left(a)) \end_layout \end_inset -? Given a value of type +. + We can produce a function of type \begin_inset listings inline true status open \begin_layout Plain Layout -A +B => C \end_layout \end_inset -, we would need to return a value of type + similarly. + The code is: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -C +def f1[A,B,C]: (Either[A, B] => C) => (A => C, B => C) = \end_layout -\end_inset - -. - The only way to obtain a value of type -\begin_inset listings -inline true -status open - \begin_layout Plain Layout -C + (h: Either[A, B] => C) => (a => h(Left(a)), b => h(Right(b))) \end_layout \end_inset - is by applying -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -k -\end_layout +We write this function in the code notation like this: +\begin_inset Formula +\begin{align*} + & f_{1}:\left(A+B\rightarrow C\right)\rightarrow\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)\quad,\\ + & f_{1}\triangleq h^{:A+B\rightarrow C}\rightarrow\big(a^{:A}\rightarrow h(a+\bbnum 0^{:B})\big)\times\big(b^{:B}\rightarrow h(\bbnum 0^{:A}+b)\big)\quad. +\end{align*} \end_inset - to some arguments. - But to apply -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -k \end_layout -\end_inset - -, we need a value of type +\begin_layout Standard +For the function \begin_inset listings inline true status open \begin_layout Plain Layout -B +f2 \end_layout \end_inset -, which we do not have. - So we cannot produce a +, we need to apply pattern matching to both curried arguments and then return + a value of type \begin_inset listings inline true status open \begin_layout Plain Layout -g: A => C +C \end_layout \end_inset . - Similarly, we cannot produce a function + This can be achieved in only one way: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -h +def f2[A,B,C](f: A => C, g: B => C): Either[A, B] => C = { \end_layout -\end_inset +\begin_layout Plain Layout - of type -\begin_inset listings -inline true -status open + case Left(a) => f(a) +\end_layout \begin_layout Plain Layout -B => C + case Right(b) => g(b) \end_layout -\end_inset +\begin_layout Plain Layout -. +} \end_layout -\begin_layout Standard -We repeat the same argument in the type notation. - Obtaining a value of type -\begin_inset Formula $(A\rightarrow C)+(B\rightarrow C)$ -\end_inset - - means to compute either -\begin_inset Formula $g^{:A\rightarrow C}+\bbnum 0$ \end_inset - or -\begin_inset Formula $\bbnum 0+h^{:B\rightarrow C}$ -\end_inset +We write this function in the code notation like this: +\begin_inset Formula +\begin{align*} + & f_{2}:\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)\rightarrow A+B\rightarrow C\quad,\\ + & f_{2}\triangleq f^{:A\rightarrow C}\times g^{:B\rightarrow C}\rightarrow\,\begin{array}{|c||c|} + & C\\ +\hline A & a\rightarrow f(a)\\ +B & b\rightarrow g(b) +\end{array}\quad. +\end{align*} -. - This decision must be hard-coded since the only data is a function -\begin_inset Formula $k^{:A\times B\rightarrow C}$ \end_inset -. - We can compute -\begin_inset Formula $g^{:A\rightarrow C}$ +The matrix in the last line has only one column because the result type, + +\begin_inset Formula $C$ \end_inset - only by partially applying -\begin_inset Formula $k^{:A\times B\rightarrow C}$ +, is not known to be a disjunctive type. + We may also simplify the functions, e.g., replace +\begin_inset Formula $a\rightarrow f(a)$ \end_inset - to a value of type -\begin_inset Formula $B$ + by just +\begin_inset Formula $f$ \end_inset -. - However, we have no values of type -\begin_inset Formula $B$ -\end_inset +, and write: +\begin_inset Formula +\[ +f_{2}\triangleq f^{:A\rightarrow C}\times g^{:B\rightarrow C}\rightarrow\,\begin{array}{|c||c|} + & C\\ +\hline A & f\\ +B & g +\end{array}\quad. +\] -. - Similarly, we cannot compute a value -\begin_inset Formula $h^{:B\rightarrow C}$ \end_inset -. -\end_layout -\begin_layout Standard -The inverse type signature -\emph on -can -\emph default - be implemented: \end_layout \begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "55col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace 32baselineskip% +It remains to verify that +\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ \end_inset - -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -def f2[A,B,C]: Either[A=>C, B=>C] => ((A,B)) => C = { -\end_layout - -\begin_layout Plain Layout - - case Left(g) => { case (a, b) => g(a) } -\end_layout - -\begin_layout Plain Layout - - case Right(h) => { case (a, b) => h(b) } -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - + and +\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -240baselineskip% +. + To compute +\begin_inset Formula $f_{1}\bef f_{2}$ \end_inset - -\end_layout +, we write (omitting types): +\begin_inset Formula +\begin{align*} +f_{1}\bef f_{2}= & \big(h\rightarrow(a\rightarrow h(a+\bbnum 0))\times(b\rightarrow h(\bbnum 0+b))\big)\bef\bigg(f\times g\rightarrow\,\begin{array}{||c|} +f\\ +g +\end{array}\,\bigg)\\ +\text{compute composition}:\quad & =h\rightarrow\,\begin{array}{||c|} +a\rightarrow h(a+\bbnum 0)\\ +b\rightarrow h(\bbnum 0+b) +\end{array}\quad. +\end{align*} \end_inset +To proceed, we need to simplify the expressions +\begin_inset Formula $h(a+\bbnum 0)$ +\end_inset -\begin_inset VSpace -120baselineskip% + and +\begin_inset Formula $h(\bbnum 0+b)$ \end_inset +. + We rewrite the argument +\begin_inset Formula $h$ +\end_inset -\end_layout + (an arbitrary function of type +\begin_inset Formula $A+B\rightarrow C$ +\end_inset -\begin_layout Standard -\noindent +) in the matrix notation: \begin_inset Formula \[ -f_{2}\triangleq\,\begin{array}{|c||c|} - & A\times B\rightarrow C\\ -\hline A\rightarrow C & g^{:A\rightarrow C}\rightarrow a\times b\rightarrow g(a)\\ -B\rightarrow C & h^{:B\rightarrow C}\rightarrow a\times b\rightarrow h(b) -\end{array}\quad. +h\triangleq\,\begin{array}{|c||c|} + & C\\ +\hline A & a\rightarrow p(a)\\ +B & b\rightarrow q(b) +\end{array}\,=\,\begin{array}{|c||c|} + & C\\ +\hline A & p\\ +B & q +\end{array}\quad, \] \end_inset - -\begin_inset VSpace -90baselineskip% +where +\begin_inset Formula $p^{:A\rightarrow C}$ \end_inset + and +\begin_inset Formula $q^{:B\rightarrow C}$ +\end_inset -\end_layout + are new arbitrary functions. + Since we already checked the types, we can omit all type annotations and + write +\begin_inset Formula $h$ +\end_inset -\begin_layout Standard -Let us now show that the logical identity: + as: \begin_inset Formula -\begin{equation} -((\alpha\wedge\beta)\Rightarrow\gamma)=((\alpha\Rightarrow\gamma)\vee(\beta\Rightarrow\gamma))\label{eq:ch-example-identity-boolean-not-constructive} -\end{equation} +\[ +h\triangleq\,\begin{array}{||c|} +p\\ +q +\end{array}\quad. +\] \end_inset -holds in Boolean logic. - A straightforward calculation is to simplify the Boolean expression using - Eq. -\begin_inset space ~ +To evaluate expressions such as +\begin_inset Formula $h(a+\bbnum 0)$ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-definition-of-implication-in-Boolean-logic" -plural "false" -caps "false" -noprefix "false" + and +\begin_inset Formula $h(\bbnum 0+b)$ +\end_inset +, we need to use one of the rows of this matrix. + The correct row will be selected +\emph on +automatically +\emph default + by the rules of matrix multiplication if we place a row vector to the left + of the matrix and use the convention of omitting terms containing +\begin_inset Formula $\bbnum 0$ \end_inset -), which only holds in Boolean logic (but not in the constructive logic). - We find: +: \begin_inset Formula -\begin{align*} -\text{left-hand side of Eq.~(\ref{eq:ch-example-identity-boolean-not-constructive})}:\quad & \left(\alpha\wedge\beta\right)\gunderline{\Rightarrow}\,\gamma\\ -\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & \quad=\gunderline{\neg(\alpha\wedge\beta)}\vee\gamma\\ -\text{use de Morgan's law}:\quad & \quad=\neg\alpha\vee\neg\beta\vee\gamma\quad.\\ -\text{right-hand side of Eq.~(\ref{eq:ch-example-identity-boolean-not-constructive})}:\quad & (\gunderline{\alpha\Rightarrow\gamma})\vee(\gunderline{\beta\Rightarrow\gamma})\\ -\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & \quad=\neg\alpha\vee\gunderline{\gamma}\vee\neg\beta\vee\gunderline{\gamma}\\ -\text{use identity }\gamma\vee\gamma=\gamma:\quad & \quad=\neg\alpha\vee\neg\beta\vee\gamma\quad. -\end{align*} - -\end_inset +\[ +\begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array}\,=a\triangleright p\quad,\quad\quad\begin{array}{|cc|} +\bbnum 0 & b\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array}\,=b\triangleright q\quad. +\] -Both sides of Eq. -\begin_inset space ~ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-identity-boolean-not-constructive" -plural "false" -caps "false" -noprefix "false" - +Here we used the symbol +\begin_inset Formula $\triangleright$ \end_inset -) are equal to the same formula, -\begin_inset Formula $\neg\alpha\vee\neg\beta\vee\gamma$ + to separate an argument from a function when the argument is written to + the +\emph on +left +\emph default + of the function. + The symbol +\begin_inset Formula $\triangleright$ \end_inset -, so the identity holds. -\end_layout - -\begin_layout Standard -This calculation does not work in the constructive logic because its proof - rules can derive neither the Boolean formula -\begin_inset space ~ + (pronounced +\begin_inset Quotes eld \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-definition-of-implication-in-Boolean-logic" -plural "false" -caps "false" -noprefix "false" - +pipe +\begin_inset Quotes erd \end_inset -) nor the -\series bold -law of de Morgan -\series default \begin_inset Index idx status open \begin_layout Plain Layout -law of de Morgan +pipe notation \end_layout \end_inset -, -\begin_inset Formula $\neg(\alpha\wedge\beta)=\left(\neg\alpha\vee\neg\beta\right)$ -\end_inset - -. -\end_layout - -\begin_layout Standard -Another way of proving the Boolean identity -\begin_inset space ~ -\end_inset - -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-identity-boolean-not-constructive" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -) is to enumerate all possible truth values for the variables -\begin_inset Formula $\alpha$ -\end_inset -, -\begin_inset Formula $\beta$ -\end_inset - -, and -\begin_inset Formula $\gamma$ -\end_inset +\begin_inset Index idx +status open -. - The left-hand side, -\begin_inset Formula $\left(\alpha\wedge\beta\right)\Rightarrow\gamma$ +\begin_layout Plain Layout +\begin_inset Formula $\triangleright$ \end_inset -, can be -\begin_inset Formula $False$ +-notation!see +\begin_inset Quotes eld \end_inset - only if -\begin_inset Formula $\alpha\wedge\beta=True$ +pipe notation +\begin_inset Quotes erd \end_inset - (that is, both -\begin_inset Formula $\alpha$ -\end_inset - and -\begin_inset Formula $\beta$ -\end_inset +\end_layout - are -\begin_inset Formula $True$ \end_inset -) and -\begin_inset Formula $\gamma=False$ +) is defined by +\begin_inset Formula $x\triangleright f\triangleq f(x)$ \end_inset . - For all other truth values of -\begin_inset Formula $\alpha$ -\end_inset - -, -\begin_inset Formula $\beta$ -\end_inset + In Scala, this operation is available as +\begin_inset listings +inline true +status open -, and -\begin_inset Formula $\gamma$ -\end_inset +\begin_layout Plain Layout -, the formula -\begin_inset Formula $\left(\alpha\wedge\beta\right)\Rightarrow\gamma$ -\end_inset +x.pipe(f) +\end_layout - is -\begin_inset Formula $True$ \end_inset -. - Let us determine when the right-hand side, -\begin_inset Formula $(\alpha\Rightarrow\gamma)\vee(\beta\Rightarrow\gamma)$ -\end_inset + as of Scala 2.13. +\end_layout -, can be -\begin_inset Formula $False$ +\begin_layout Standard +We can write values of disjunctive types, such as +\begin_inset Formula $a+\bbnum 0$ \end_inset -. - This can happen only if both parts of the disjunction are -\begin_inset Formula $False$ +, as row vectors +\begin_inset Formula $\,\begin{array}{|cc|} +a & \bbnum 0\end{array}\,$ \end_inset -. - That means -\begin_inset Formula $\alpha=True$ -\end_inset +: +\begin_inset Formula +\begin{equation} +h(a+\bbnum 0)=(a+\bbnum 0)\triangleright h=\,\begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright\,h\quad.\label{eq:forward-notation-} +\end{equation} -, -\begin_inset Formula $\beta=True$ \end_inset -, and -\begin_inset Formula $\gamma=False$ +With these notations, we compute further. + Omit all terms applying +\begin_inset Formula $\bbnum 0$ \end_inset -. - So, the two sides of the identity -\begin_inset space ~ + or applying something to +\begin_inset Formula $\bbnum 0$ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-identity-boolean-not-constructive" -plural "false" -caps "false" -noprefix "false" +: +\begin_inset Formula +\begin{align*} + & \begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright\,h=\,\begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array}\,=a\triangleright p=p(a)\quad,\\ + & \begin{array}{|cc|} +\bbnum 0 & b\end{array}\,\triangleright\,h=\,\,\begin{array}{|cc|} +\bbnum 0 & b\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array}\,=b\triangleright q=q(b)\quad. +\end{align*} \end_inset -) are both -\begin_inset Formula $True$ +Now we can complete the proof of +\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ \end_inset - or both -\begin_inset Formula $False$ -\end_inset +: +\begin_inset Formula +\begin{align*} +f_{1}\bef f_{2} & =h\rightarrow\,\begin{array}{||c|} +a\rightarrow h(a+\bbnum 0)\\ +b\rightarrow h(\bbnum 0+b) +\end{array}\\ +\text{previous equations}:\quad & =\,\begin{array}{||c|} +p\\ +q +\end{array}\,\rightarrow\,\begin{array}{||c|} +a\rightarrow p(a)\\ +b\rightarrow q(b) +\end{array}\\ +\text{simplify functions}:\quad & =\,\,\begin{array}{||c|} +p\\ +q +\end{array}\,\rightarrow\,\begin{array}{||c|} +p\\ +q +\end{array}\,=\text{id}\quad. +\end{align*} - with any choice of truth values of -\begin_inset Formula $\alpha$ \end_inset -, -\begin_inset Formula $\beta$ -\end_inset -, and -\begin_inset Formula $\gamma$ +\end_layout + +\begin_layout Standard +To prove that +\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ \end_inset -. - In Boolean logic, this is sufficient to prove the identity +, use the notation \begin_inset space ~ \end_inset ( \begin_inset CommandInset ref LatexCommand ref -reference "eq:ch-example-identity-boolean-not-constructive" +reference "eq:forward-notation-" plural "false" caps "false" noprefix "false" \end_inset -). - -\begin_inset Formula $\square$ +): +\begin_inset Formula +\begin{align*} +f_{2}\bef f_{1}= & \bigg(f\times g\rightarrow\,\begin{array}{||c|} +f\\ +g +\end{array}\,\bigg)\bef\big(h\rightarrow(a\rightarrow(a+\bbnum 0)\triangleright h)\times(b\rightarrow(\bbnum 0+b)\triangleright h)\big)\\ +\text{composition}:\quad & =f\times g\rightarrow\big(a\rightarrow\gunderline{\,\begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright}\,\begin{array}{||c|} +f\\ +g +\end{array}\,\big)\times\big(b\rightarrow\gunderline{\,\begin{array}{|cc|} +\bbnum 0 & b\end{array}\,\triangleright}\,\begin{array}{||c|} +f\\ +g +\end{array}\,\big)\\ +\text{apply functions}:\quad & =f\times g\rightarrow(a\rightarrow\gunderline{a\triangleright f})\times(b\rightarrow\gunderline{b\triangleright g})\\ +\text{definition of }\triangleright:\quad & =f\times g\rightarrow\gunderline{\left(a\rightarrow f(a)\right)}\times\gunderline{\left(b\rightarrow g(b)\right)}\\ +\text{simplify functions}:\quad & =\left(f\times g\rightarrow f\times g\right)=\text{id}\quad. +\end{align*} + \end_inset \end_layout \begin_layout Standard -The following example shows how to use the formulas from Tables -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Logical-identities-with-disjunction-and-conjunction" -plural "false" -caps "false" -noprefix "false" - +In this way, we have proved that +\begin_inset Formula $f_{1}$ \end_inset -– -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Logical-identities-with-function-types" -plural "false" -caps "false" -noprefix "false" - + and +\begin_inset Formula $f_{2}$ \end_inset - to derive the type equivalence of complicated type expressions without - need for proofs. -\end_layout - -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-solvedExample-5-2" - + are mutual inverses. + The proofs appear long because we took time to motivate and introduce new + notation for applying matrices to row vectors. + Once this notation is understood, the proof for +\begin_inset Formula $f_{1}\bef f_{2}=\text{id}$ \end_inset - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-solvedExample-5-2" -plural "false" -caps "false" -noprefix "false" + can be written as: +\begin_inset Formula +\begin{align*} +f_{1}\bef f_{2}= & \left(h\rightarrow(a\rightarrow(a+\bbnum 0)\triangleright h)\times(b\rightarrow(\bbnum 0+b)\triangleright h)\right)\bef\bigg(f\times g\rightarrow\,\begin{array}{||c|} +f\\ +g +\end{array}\bigg)\\ +\text{composition}:\quad & =h\rightarrow\,\begin{array}{||c|} +\,a\,\rightarrow\,\left|\begin{array}{cc} +a & \bbnum 0\end{array}\right|\triangleright h\\ +b\rightarrow\left|\begin{array}{cc} +\bbnum 0 & b\end{array}\right|\triangleright h +\end{array}\,=\,\begin{array}{||c|} +p\\ +q +\end{array}\rightarrow\,\begin{array}{||c|} +a\rightarrow\,\begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array}\\ +b\,\rightarrow\,\begin{array}{|cc|} +\bbnum 0 & b\,\,\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array} +\end{array}\\ +\text{apply functions}:\quad & =\,\begin{array}{||c|} +p\\ +q +\end{array}\,\rightarrow\,\begin{array}{||c|} +a\rightarrow a\triangleright p\\ +b\rightarrow b\triangleright q +\end{array}\,=\,\begin{array}{||c|} +p\\ +q +\end{array}\,\rightarrow\,\begin{array}{||c|} +p\\ +q +\end{array}\,=\text{id}\quad. +\end{align*} \end_inset +Proofs in the code notation are shorter than in Scala syntax because certain + names and keywords (such as +\begin_inset listings +inline true +status open -\end_layout +\begin_layout Plain Layout -\begin_layout Standard -Use known formulas to verify the type equivalences without direct proofs: +Left \end_layout -\begin_layout Standard - -\series bold -(a) -\series default - -\begin_inset Formula $A\times\left(A+\bbnum 1\right)\times\left(A+\bbnum 1+\bbnum 1\right)\cong A\times\left(\bbnum 1+\bbnum 1+A\times\left(\bbnum 1+\bbnum 1+\bbnum 1+A\right)\right)$ \end_inset -. -\end_layout - -\begin_layout Standard - -\series bold -(b) -\series default - -\begin_inset Formula $\bbnum 1+A+B\rightarrow\bbnum 1\times B\cong\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B$ -\end_inset +, +\begin_inset listings +inline true +status open -. -\end_layout +\begin_layout Plain Layout -\begin_layout Subparagraph -Solution +Right \end_layout -\begin_layout Standard - -\series bold -(a) -\series default - We can expand brackets in type expressions as in arithmetic: -\begin_inset Formula -\begin{align*} -A\times\left(A+\bbnum 1\right) & \cong A\times A+A\times\bbnum 1\cong A\times A+A\quad,\\ -A\times\left(A+\bbnum 1\right)\times\left(A+\bbnum 1+\bbnum 1\right) & \cong\left(A\times A+A\right)\times\left(A+\bbnum 1+\bbnum 1\right)\\ - & \cong A\times A\times A+A\times A+A\times A\times\left(\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\\ - & \cong A\times A\times A+A\times A\times\left(\bbnum 1+\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\quad. -\end{align*} - -\end_inset - -The result looks like a polynomial in -\begin_inset Formula $A$ \end_inset -, which we can now rearrange into the required form: -\begin_inset Formula -\[ -A\times A\times A+A\times A\times\left(\bbnum 1+\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\cong A\times\left(\bbnum 1+\bbnum 1+A\times\left(\bbnum 1+\bbnum 1+\bbnum 1+A\right)\right)\quad. -\] - -\end_inset +, +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +case \end_layout -\begin_layout Standard - -\series bold -(b) -\series default - Keep in mind that the conventions of the type notation make the function - arrow -\begin_inset Formula $\left(\rightarrow\right)$ \end_inset - group weaker than other type operations. - So, the type expression -\begin_inset Formula $\bbnum 1+A+B\rightarrow\bbnum 1\times B$ -\end_inset +, +\begin_inset listings +inline true +status open - means a function from -\begin_inset Formula $\bbnum 1+A+B$ -\end_inset +\begin_layout Plain Layout + +match +\end_layout - to -\begin_inset Formula $\bbnum 1\times B$ \end_inset -. - +, etc.) are omitted. + From now on, we will prefer to use the code notation in proofs, keeping + in mind that one can always convert the code notation to Scala. \end_layout \begin_layout Standard -Begin by using the equivalence -\begin_inset Formula $\bbnum 1\times B\cong B$ +Note that the function arrow ( +\begin_inset Formula $\rightarrow$ \end_inset - to obtain -\begin_inset Formula $\bbnum 1+A+B\rightarrow B$ +) binds weaker than the pipe operation ( +\begin_inset Formula $\triangleright$ \end_inset -. - Now we use another rule: -\begin_inset Formula -\[ -A+B\rightarrow C\cong\left(A\rightarrow C\right)\times\left(B\rightarrow C\right) -\] - +), so the code notation +\begin_inset Formula $x\rightarrow y\triangleright z$ \end_inset -and derive the equivalence: -\begin_inset Formula -\[ -\bbnum 1+A+B\rightarrow B\cong\left(\bbnum 1\rightarrow B\right)\times\left(A\rightarrow B\right)\times\left(B\rightarrow B\right)\quad. -\] - + means +\begin_inset Formula $x\rightarrow(y\triangleright z)$ \end_inset -Finally, we note that -\begin_inset Formula $\bbnum 1\rightarrow B\cong B$ +. + We will review the code notation more systematically in Chapter +\begin_inset space ~ \end_inset - and that the type product is commutative, so we can rearrange the last - type expression into the required form: -\begin_inset Formula -\[ -B\times\left(A\rightarrow B\right)\times\left(B\rightarrow B\right)\cong\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B\quad. -\] -\end_inset +\begin_inset CommandInset ref +LatexCommand ref +reference "chap:Reasoning-about-code" +plural "false" +caps "false" +noprefix "false" -We obtain the required type expression: -\begin_inset Formula $\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B$ \end_inset . @@ -22919,14 +22883,14 @@ We obtain the required type expression: Example \begin_inset CommandInset label LatexCommand label -name "subsec:ch-solvedExample-5" +name "subsec:ch-Example-type-identity-6" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-solvedExample-5" +reference "subsec:ch-Example-type-identity-6" plural "false" caps "false" noprefix "false" @@ -22937,19 +22901,15 @@ noprefix "false" \end_layout \begin_layout Standard -Denote -\begin_inset Formula $\text{Read}^{E,A}\triangleq E\rightarrow A$ -\end_inset +Verify the type equivalence: +\begin_inset Formula +\[ +A\times B\rightarrow C\cong A\rightarrow B\rightarrow C\quad. +\] - and implement fully parametric functions with types -\begin_inset Formula $A\rightarrow\text{Read}^{E,A}$ \end_inset - and -\begin_inset Formula $\text{Read}^{E,A}\rightarrow(A\rightarrow B)\rightarrow\text{Read}^{E,B}$ -\end_inset -. \end_layout \begin_layout Subparagraph @@ -22957,644 +22917,602 @@ Solution \end_layout \begin_layout Standard -Begin by defining a type alias for the type constructor -\begin_inset Formula $\text{Read}^{E,A}$ -\end_inset - -: +Begin by implementing the two functions: \begin_inset listings inline false status open \begin_layout Plain Layout -type Read[E, A] = E => A +def f1[A,B,C]: (((A, B)) => C) => A => B => C = ??? +\end_layout + +\begin_layout Plain Layout + +def f2[A,B,C]: (A => B => C) => ((A, B)) => C = ??? \end_layout \end_inset -The first type signature has only one implementation: +The Scala code can be derived from the type signatures unambiguously: \begin_inset listings inline false status open \begin_layout Plain Layout -def p[E, A]: A => Read[E, A] = { x => _ => x } +def f1[A,B,C]: (((A, B)) => C) => A => B => C = g => a => b => g((a, b)) \end_layout -\end_inset +\begin_layout Plain Layout -We -\emph on -must -\emph default - discard the argument of type -\begin_inset Formula $E$ -\end_inset +def f2[A,B,C]: (A => B => C) => ((A, B)) => C = h => { case (a, b) => h(a)(b) + } +\end_layout -; we cannot use it for computing a value of type -\begin_inset listings -inline true -status open +\end_inset -\begin_layout Plain Layout +Write these functions in the code notation: +\begin_inset Formula +\begin{align*} + & f_{1}=g^{:A\times B\rightarrow C}\rightarrow a^{:A}\rightarrow b^{:B}\rightarrow g(a\times b)\quad,\\ + & f_{2}=h^{:A\rightarrow B\rightarrow C}\rightarrow\left(a\times b\right)^{:A\times B}\rightarrow h(a)(b)\quad. +\end{align*} -A -\end_layout +\end_inset +We denote by +\begin_inset Formula $\left(a\times b\right)^{:A\times B}$ \end_inset - given + the argument of type \begin_inset listings inline true status open \begin_layout Plain Layout -x:A +(A, B) \end_layout \end_inset -. + with pattern matching implied. + This notation allows us to write shorter code formulas involving tupled + arguments. \end_layout \begin_layout Standard -The second type signature has three type parameters. - It is the curried version of the function -\begin_inset listings -inline true -status open +Compute the function composition +\begin_inset Formula $f_{1}\bef f_{2}$ +\end_inset -\begin_layout Plain Layout + and show that it is equal to an identity function: +\begin_inset Formula +\begin{align*} +\text{expect to equal }\text{id}:\quad & f_{1}\bef f_{2}=(g\rightarrow\gunderline{a\rightarrow b\rightarrow g(a\times b)})\bef\left(h\rightarrow a\times b\rightarrow h(a)(b)\right)\\ +\text{composition}:\quad & \quad=g\rightarrow\gunderline{a\times b\rightarrow g(a\times b)}\\ +\text{simplify function}:\quad & \quad=\left(g\rightarrow g\right)=\text{id}\quad. +\end{align*} -map -\end_layout +\end_inset +Compute the function composition +\begin_inset Formula $f_{2}\bef f_{1}$ \end_inset -: -\begin_inset listings -inline false -status open + and show that it is equal to an identity function: +\begin_inset Formula +\begin{align*} +\text{expect to equal }\text{id}:\quad & f_{2}\bef f_{1}=(h\rightarrow\gunderline{a\times b\rightarrow h(a)(b)})\bef\left(g\rightarrow a\rightarrow b\rightarrow g(a\times b)\right)\\ +\text{composition}:\quad & \quad=h\rightarrow a\rightarrow\gunderline{b\rightarrow h(a)(b)}\\ +\text{simplify }b\rightarrow h(a)(b):\quad & \quad=h\rightarrow\gunderline{a\rightarrow h(a)}\\ +\text{simplify }a\rightarrow h(a)\text{ to }h:\quad & \quad=\left(h\rightarrow h\right)=\text{id}\quad. +\end{align*} + +\end_inset -\begin_layout Plain Layout -def map[E, A, B]: Read[E, A] => (A => B) => Read[E, B] = ??? \end_layout -\end_inset +\begin_layout Section +Summary +\end_layout -Expanding the type alias, we see that the two curried arguments are functions - of types -\begin_inset Formula $E\rightarrow A$ -\end_inset +\begin_layout Standard +What tasks can we perform now? +\end_layout - and -\begin_inset Formula $A\rightarrow B$ -\end_inset +\begin_layout Itemize +Convert a fully parametric type signature into a logical formula and: +\end_layout -. - The forward composition of these functions is a function of type -\begin_inset Formula $E\rightarrow B$ +\begin_deeper +\begin_layout Itemize +Decide whether the type signature can be implemented in code. +\end_layout + +\begin_layout Itemize +If possible, derive the code using the CH correspondence. +\end_layout + +\end_deeper +\begin_layout Itemize +Use the type notation (Table +\begin_inset space ~ \end_inset -, or -\begin_inset Formula $\text{Read}^{E,B}$ + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:ch-correspondence-type-notation-CH-propositions" +plural "false" +caps "false" +noprefix "false" + \end_inset -, which is exactly what we are required to return. - So, the code can be written as: +) for reasoning about types to: \end_layout -\begin_layout Standard -\begin_inset listings -inline false -status open +\begin_deeper +\begin_layout Itemize +Decide type equivalence using the rules in Tables +\begin_inset space ~ +\end_inset -\begin_layout Plain Layout -def map[E, A, B]: (E => A) => (A => B) => E => B = { r => f => r andThen - f } -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Logical-identities-with-disjunction-and-conjunction" +plural "false" +caps "false" +noprefix "false" \end_inset -If we did not notice this shortcut, we would reason differently: We are - required to compute a value of type -\begin_inset Formula $B$ +– +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Logical-identities-with-function-types" +plural "false" +caps "false" +noprefix "false" + \end_inset - given +. +\end_layout + +\begin_layout Itemize +Simplify type expressions before writing code. +\end_layout + +\end_deeper +\begin_layout Itemize +Use the matrix notation and the pipe notation to write code that works on + disjunctive types. +\end_layout + +\begin_layout Standard +What tasks \emph on -three +cannot \emph default - curried arguments -\begin_inset Formula $r^{:E\rightarrow A}$ -\end_inset + be performed with these tools? +\end_layout -, -\begin_inset Formula $f^{:A\rightarrow B}$ +\begin_layout Itemize +Automatically generate code for +\emph on +recursive +\emph default + functions. + The CH correspondence is based on propositional logic, which cannot describe + recursion. + Accordingly, recursion is absent from the eight code constructions of Section +\begin_inset space ~ \end_inset -, and -\begin_inset Formula $e^{:E}$ + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:The-rules-of-proof" +plural "false" +caps "false" +noprefix "false" + \end_inset . - Write this requirement as: -\begin_inset Formula -\[ -\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow???^{:B}\quad, -\] + Recursive functions need to be coded by hand. +\end_layout + +\begin_layout Itemize +Automatically generate code satisfying a property (e.g., isomorphism). + We may generate some code, but the CH correspondence does not guarantee + that properties will hold. + We need to verify the required properties manually, after deriving the + code. +\end_layout +\begin_layout Itemize +Express complicated conditions (e.g., +\begin_inset Quotes eld \end_inset -The symbol -\begin_inset Formula $\text{???}^{:B}$ +array is sorted +\begin_inset Quotes erd \end_inset - is called a +) in a type signature. + This can be done using +\series bold +dependent types +\series default + \begin_inset Index idx status open \begin_layout Plain Layout -typed hole +dependent type \end_layout \end_inset + (i.e., types that directly depend on values in some way). + This is an advanced technique beyond the scope of this book. + Programming languages such as Coq, Agda, and Idris fully support dependent + types, while Scala has only limited support. +\end_layout -\series bold -typed hole -\series default -. - It stands for a value that we are still figuring out how to compute, but - whose type is already known. - Typed holes are supported in Scala by an experimental compiler plugin. -\begin_inset Foot +\begin_layout Itemize +Generate code using type constructors with known methods (e.g., the +\begin_inset listings +inline true status open \begin_layout Plain Layout -See -\family typewriter - -\begin_inset CommandInset href -LatexCommand href -name "https://github.com/cb372/scala-typed-holes" -target "https://github.com/cb372/scala-typed-holes" -literal "false" - -\end_inset - +map \end_layout \end_inset - The plugin will print the known information about the typed hole. + method). \end_layout \begin_layout Standard -To fill the typed hole -\begin_inset Formula $\text{???}^{:B}$ -\end_inset +As an example of using type constructors with known methods, consider this + type signature: +\begin_inset listings +inline false +status open -, we need a value of type -\begin_inset Formula $B$ -\end_inset +\begin_layout Plain Layout -. - Since no arguments have type -\begin_inset Formula $B$ -\end_inset +def q[A]: Array[A] => (A => Option[B]) => Array[Option[B]] +\end_layout -, the only way of getting a value of type -\begin_inset Formula $B$ \end_inset - is to apply -\begin_inset Formula $f^{:A\rightarrow B}$ -\end_inset +Can we generate the code of this function from its type signature? We know + that the Scala library defines a +\begin_inset listings +inline true +status open - to some value of type -\begin_inset Formula $A$ -\end_inset +\begin_layout Plain Layout -. - So we write: -\begin_inset Formula -\[ -\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(???^{:A})\quad. -\] +map +\end_layout \end_inset -The only way of getting an -\begin_inset Formula $A$ -\end_inset - - is to apply -\begin_inset Formula $r$ -\end_inset + method on the +\begin_inset listings +inline true +status open - to some value of type -\begin_inset Formula $E$ -\end_inset +\begin_layout Plain Layout -: -\begin_inset Formula -\[ -\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(r(???^{:E}))\quad. -\] +Array +\end_layout \end_inset -We have exactly one value of type -\begin_inset Formula $E$ -\end_inset + class. + So, an implementation of +\begin_inset listings +inline true +status open -, namely -\begin_inset Formula $e^{:E}$ -\end_inset +\begin_layout Plain Layout -. - So, the code must be: -\begin_inset Formula -\[ -\text{map}^{E,A,B}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(r(e))\quad. -\] +q +\end_layout \end_inset -Translate this to the Scala syntax: + is: \begin_inset listings inline false status open \begin_layout Plain Layout -def map[E, A, B]: (E => A) => (A => B) => E => B = { r => f => e => f(r(e)) - } +def q[A]: Array[A] => (A => Option[B]) => Array[Option[B]] = { arr => f + => arr.map(f) } \end_layout \end_inset -We may now notice that the expression -\begin_inset Formula $e\rightarrow f(r(e))$ -\end_inset - - is a function composition -\begin_inset Formula $r\bef f$ -\end_inset +However, it is hard to create an +\emph on +algorithm +\emph default + that can derive this implementation automatically from the type signature + of +\begin_inset listings +inline true +status open - applied to -\begin_inset Formula $e$ -\end_inset +\begin_layout Plain Layout -, and simplify the code accordingly. +q \end_layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-solvedExample-6" - -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-solvedExample-6" -plural "false" -caps "false" -noprefix "false" - \end_inset - -\end_layout - -\begin_layout Standard -Show that the type signature + via the Curry-Howard correspondence. + The algorithm would have to convert the type signature of \begin_inset listings inline true status open \begin_layout Plain Layout -Read[A, T] => (A => B) => Read[B, T] +q \end_layout \end_inset - cannot be implemented as a fully parametric function. -\end_layout + into this logical formula: +\begin_inset Formula +\begin{equation} +{\cal CH}(\text{Array}^{A})\Rightarrow{\cal CH}(A\rightarrow\text{Opt}^{B})\Rightarrow{\cal CH}(\text{Array}^{\text{Opt}^{B}})\quad.\label{eq:ch-example-quantified-proposition} +\end{equation} -\begin_layout Subparagraph -Solution -\end_layout +\end_inset -\begin_layout Standard -Expand the type signature and try implementing this function: +To derive an implementation, the algorithm would need to use the available + \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def m[A, B, T] : (A => T) => (A => B) => B => T = { r => f => b => ??? } +map \end_layout \end_inset -Given values -\begin_inset Formula $r^{:A\rightarrow T}$ -\end_inset + method for +\begin_inset listings +inline true +status open -, -\begin_inset Formula $f^{:A\rightarrow B}$ -\end_inset +\begin_layout Plain Layout -, and -\begin_inset Formula $b^{:B}$ -\end_inset +Array +\end_layout -, we need to compute a value of type -\begin_inset Formula $T$ \end_inset -: +. + That method has the type signature: \begin_inset Formula \[ -m=r^{:A\rightarrow T}\rightarrow f^{:A\rightarrow B}\rightarrow b^{:B}\rightarrow???^{:T}\quad. +\text{map}:\forall(A,B).\,\text{Array}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Array}^{B}\quad. \] \end_inset -The only way of getting a value of type -\begin_inset Formula $T$ +To derive the +\begin_inset Formula ${\cal CH}$ \end_inset - is to apply -\begin_inset Formula $r$ +-proposition +\begin_inset space ~ \end_inset - to some value of type -\begin_inset Formula $A$ +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-quantified-proposition" +plural "false" +caps "false" +noprefix "false" + \end_inset -: +), the algorithm will need to assume that the +\begin_inset Formula ${\cal CH}$ +\end_inset + +-proposition: \begin_inset Formula -\[ -m=r^{:A\rightarrow T}\rightarrow f^{:A\rightarrow B}\rightarrow b^{:B}\rightarrow r(???^{:A})\quad. -\] +\begin{equation} +{\cal CH}\,\big(\forall(A,B).\,\text{Array}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Array}^{B}\big)\label{eq:ch-example-quantified-proposition-2} +\end{equation} \end_inset -However, we do not have any values of type -\begin_inset Formula $A$ +already holds. + In other words Eq. +\begin_inset space ~ \end_inset -. - We have a function -\begin_inset Formula $f^{:A\rightarrow B}$ -\end_inset +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-quantified-proposition-2" +plural "false" +caps "false" +noprefix "false" - that -\emph on -consumes -\emph default - values of type -\begin_inset Formula $A$ \end_inset -, and we cannot use -\begin_inset Formula $f$ +) is one of the premises of a sequent. + Reasoning about premises such as Eq. +\begin_inset space ~ \end_inset - to produce any values of type -\begin_inset Formula $A$ -\end_inset +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-quantified-proposition-2" +plural "false" +caps "false" +noprefix "false" -. - So, it seems that we are unable to fill the typed hole -\begin_inset Formula $\text{???}^{:A}$ \end_inset - and implement the function -\begin_inset listings -inline true +) requires +\begin_inset Index idx status open \begin_layout Plain Layout - -m +first-order logic \end_layout \end_inset -. -\end_layout - -\begin_layout Standard -In order to verify that -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -m -\end_layout +\series bold +first-order logic +\series default + — a logic whose proof rules can handle quantified types such as +\begin_inset Formula $\forall(A,B)$ \end_inset - is unimplementable, we need to prove that the logical formula: -\begin_inset Formula -\begin{equation} -\forall(\alpha,\beta,\tau).\,(\alpha\Rightarrow\tau)\Rightarrow(\alpha\Rightarrow\beta)\Rightarrow(\beta\Rightarrow\tau)\label{eq:ch-example-boolean-formula-3} -\end{equation} -\end_inset +\emph on + +\emph default +inside premises. + However, first-order logic is +\series bold +undecidable +\series default -is not true in the constructive logic. - We could use the -\begin_inset listings -inline true +\begin_inset Index idx status open \begin_layout Plain Layout - -curryhoward +undecidable logic \end_layout \end_inset - library for that: -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -@ def m[A, B, T] : (A => T) => (A => B) => B => T = implement -\end_layout - -\begin_layout Plain Layout - -cmd1.sc:1: type (A => T) => (A => B) => B => T cannot be implemented -\end_layout - -\begin_layout Plain Layout - -def m[A, B, T] : (A => T) => (A => B) => B => T = implement -\end_layout - -\begin_layout Plain Layout - - ^ -\end_layout - -\begin_layout Plain Layout - -Compilation Failed +: no algorithm can find a proof (or verify the absence of a proof) in all + cases. + \end_layout -\end_inset - -Another way is to check whether this formula is true in Boolean logic. - A formula that holds in constructive logic will always hold in Boolean - logic, because all rules shown in Section +\begin_layout Standard +The constructive propositional logic with the rules listed in Table \begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:The-rules-of-proof" +reference "tab:Proof-rules-for-constructive-logic" plural "false" caps "false" noprefix "false" \end_inset - preserve Boolean truth values (see Section -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Relationship-between-Boolean" -plural "false" -caps "false" -noprefix "false" - -\end_inset + is +\series bold +decidable +\series default +, +\begin_inset Index idx +status open - for a proof). - It follows that any formula that fails to hold in Boolean logic will also - not hold in constructive logic. - +\begin_layout Plain Layout +decidable logic \end_layout -\begin_layout Standard -It is relatively easy to check whether a given Boolean formula is always - equal to -\begin_inset Formula $True$ -\end_inset - -. - Simplifying Eq. -\begin_inset space ~ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-boolean-formula-3" -plural "false" -caps "false" -noprefix "false" - + i.e., it has an algorithm that either finds a proof or disproves any given + formula. + However, that logic cannot handle type constructors such as +\begin_inset Formula $\text{Array}^{A}$ \end_inset -) with the rules of Boolean logic, we find: -\begin_inset Formula -\begin{align*} - & (\alpha\Rightarrow\tau)\,\gunderline{\Rightarrow}\,(\alpha\Rightarrow\beta)\,\gunderline{\Rightarrow}\,(\beta\Rightarrow\tau)\\ -\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & =\neg(\gunderline{\alpha\Rightarrow\tau})\vee\neg(\gunderline{\alpha\Rightarrow\beta})\vee(\gunderline{\beta\Rightarrow\tau})\\ -\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & =\gunderline{\neg(\neg\alpha\vee\tau)}\vee\gunderline{\neg(\neg\alpha\vee\beta)}\vee(\neg\beta\vee\tau)\\ -\text{use de Morgan's law}:\quad & =\left(\alpha\wedge\neg\tau\right)\vee\gunderline{\left(\alpha\wedge\neg\beta\right)\vee\neg\beta}\vee\tau\\ -\text{use identity }(p\wedge q)\vee q=q:\quad & =\gunderline{\left(\alpha\wedge\neg\tau\right)}\vee\neg\beta\vee\gunderline{\tau}\\ -\text{use identity }(p\wedge\neg q)\vee q=p\vee q:\quad & =\alpha\vee\neg\beta\vee\tau\quad. -\end{align*} - +. + It also cannot handle premises containing type quantifiers such as +\begin_inset Formula $\forall(A,B)$ \end_inset -This formula is not identically -\begin_inset Formula $True$ -\end_inset +, because all the available rules have the quantifiers placed +\emph on +outside +\emph default + the premises. + +\end_layout -: it is -\begin_inset Formula $False$ -\end_inset +\begin_layout Standard +So, code for functions such as +\begin_inset listings +inline true +status open - when -\begin_inset Formula $\alpha=\tau=False$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $\beta=True$ -\end_inset +q +\end_layout -. - So, Eq. -\begin_inset space ~ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-boolean-formula-3" -plural "false" -caps "false" -noprefix "false" - -\end_inset + can only be derived by trial and error, informed by intuition. + This book will help programmers to acquire the necessary intuition and + technique. +\end_layout -) is not true in Boolean logic, therefore it is also not true in constructive - logic. - By the CH correspondence, we conclude that the type signature of -\begin_inset listings -inline true +\begin_layout Subsection +Examples +\begin_inset Index idx status open \begin_layout Plain Layout - -m +examples (with code) \end_layout \end_inset - cannot be implemented by a fully parametric function. + \end_layout \begin_layout Subsubsection Example \begin_inset CommandInset label LatexCommand label -name "subsec:ch-solvedExample-7" +name "subsec:ch-solvedExample-1" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-solvedExample-7" +reference "subsec:ch-solvedExample-1" plural "false" caps "false" noprefix "false" @@ -23605,681 +23523,823 @@ noprefix "false" \end_layout \begin_layout Standard -Define the type constructor -\begin_inset Formula $P^{A}\triangleq\bbnum 1+A+A$ +Find the cardinality of the type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +P = Option[Option[Boolean] => Boolean] +\end_layout + \end_inset - and implement +. + Write \begin_inset listings inline true status open \begin_layout Plain Layout -map +P \end_layout \end_inset - for it: -\begin_inset Formula -\[ -\text{map}^{A,B}:P^{A}\rightarrow(A\rightarrow B)\rightarrow P^{B}\quad. -\] + in the type notation and simplify it to an equivalent type. +\end_layout -\end_inset +\begin_layout Subparagraph +Solution +\end_layout -To check that +\begin_layout Standard +Begin with the type \begin_inset listings inline true status open \begin_layout Plain Layout -map +Option[Boolean] \end_layout \end_inset - preserves information, verify the law +, which can be either \begin_inset listings inline true status open \begin_layout Plain Layout -map(p)(x => x) == p +None \end_layout \end_inset - for all + or \begin_inset listings inline true status open \begin_layout Plain Layout -p: P[A] +Some(x) \end_layout \end_inset -. -\end_layout + with some value +\begin_inset listings +inline true +status open -\begin_layout Subparagraph -Solution +\begin_layout Plain Layout + +x: Boolean \end_layout -\begin_layout Standard -It is implied that +\end_inset + +. + Because the type \begin_inset listings inline true status open \begin_layout Plain Layout -map +Boolean \end_layout \end_inset - should be fully parametric and information-preserving. - Begin by defining a Scala type constructor for the notation -\begin_inset Formula $P^{A}\triangleq\bbnum 1+A+A$ + has +\begin_inset Formula $2$ \end_inset -: + possible values, the type \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -sealed trait P[A] +Option[Boolean] \end_layout -\begin_layout Plain Layout +\end_inset -final case class P1[A]() extends P[A] -\end_layout + has +\begin_inset Formula $3$ +\end_inset -\begin_layout Plain Layout + possible values: +\begin_inset Formula +\[ +|\text{Opt}^{\text{Boolean}}|=\left|\bbnum 1+\text{Boolean}\right|=1+\left|\text{Boolean}\right|=1+2=3\quad. +\] -final case class P2[A](x: A) extends P[A] -\end_layout +\end_inset + +In the type notation, +\begin_inset listings +inline true +status open \begin_layout Plain Layout -final case class P3[A](x: A) extends P[A] +Boolean \end_layout \end_inset -Now we can write code to implement the required type signature. - Each time we have several choices of an implementation, we will choose - to preserve information as much as possible. -\end_layout + is denoted by the symbol +\begin_inset Formula $\bbnum 2$ +\end_inset -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" + and +\begin_inset listings +inline true status open \begin_layout Plain Layout -\begin_inset VSpace -0baselineskip% + +Option[Boolean] +\end_layout + \end_inset + by +\begin_inset Formula $\bbnum 1+\bbnum 2$ +\end_inset -\end_layout +. + So, the type notation +\begin_inset Formula $\bbnum 1+\bbnum 2$ +\end_inset -\begin_layout Plain Layout -\begin_inset listings -inline false -status open + is consistent with the cardinality +\begin_inset Formula $3$ +\end_inset -\begin_layout Plain Layout + of that type: +\begin_inset Formula +\[ +\left|\bbnum 1+\text{Boolean}\right|=\left|\bbnum 1+\bbnum 2\right|=1+2=3\quad. +\] -def map[A, B]: P[A] => (A => B) => P[B] = -\end_layout +\end_inset -\begin_layout Plain Layout - p => f => p match { \end_layout +\begin_layout Standard +The function type +\begin_inset listings +inline true +status open + \begin_layout Plain Layout - case P1() => P1() // No other choice. +Option[Boolean] => Boolean \end_layout -\begin_layout Plain Layout +\end_inset - case P2(x) => ??? -\end_layout + is denoted by +\begin_inset Formula $\bbnum 1+\bbnum 2\rightarrow\bbnum 2$ +\end_inset -\begin_layout Plain Layout +. + Compute its cardinality as: +\begin_inset Formula +\[ +|\text{Opt}^{\text{Boolean}}\rightarrow\text{Boolean}|=\left|\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right|=\left|\bbnum 2\right|^{\left|\bbnum 1+\bbnum 2\right|}=2^{3}=8\quad. +\] - case P3(x) => ??? -\end_layout +\end_inset + +Finally, the we write +\begin_inset listings +inline true +status open \begin_layout Plain Layout - } +P \end_layout \end_inset + in the type notation as +\begin_inset Formula $P=\bbnum 1+\left(\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right)$ +\end_inset + + and find: +\begin_inset Formula +\[ +\left|P\right|=\left|\bbnum 1+\left(\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right)\right|=1+\left|\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right|=1+8=9\quad. +\] -\begin_inset VSpace -90baselineskip% \end_inset \end_layout +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-solvedExample-2" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-solvedExample-2" +plural "false" +caps "false" +noprefix "false" + \end_inset \end_layout \begin_layout Standard -\noindent -In the case +Implement a Scala type \begin_inset listings inline true status open \begin_layout Plain Layout -P2(x) +P[A] \end_layout \end_inset -, we are required to produce a value of type -\begin_inset Formula $P^{B}$ -\end_inset + given by this type notation: +\begin_inset Formula +\[ +P^{A}\triangleq1+A+\text{Int}\times A+(\text{String}\rightarrow A)\quad. +\] - from a value -\begin_inset Formula $x^{:A}$ \end_inset - and a function -\begin_inset Formula $f^{:A\rightarrow B}$ -\end_inset -. - Since -\begin_inset Formula $P^{B}$ -\end_inset +\end_layout - is a disjunctive type with three parts, we can produce a value of type - -\begin_inset Formula $P^{B}$ +\begin_layout Subparagraph +Solution +\end_layout + +\begin_layout Standard +To translate type notation into Scala code, begin by defining the disjunctive + types as case classes, choosing class names for convenience. + In this case, +\begin_inset Formula $P^{A}$ \end_inset - in three different ways: + is a disjunctive type with four parts, so we need four case classes: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -P1() +sealed trait P[A] \end_layout -\end_inset +\begin_layout Plain Layout -, -\begin_inset listings -inline true -status open +final case class P1[A](???) extends P[A] +\end_layout \begin_layout Plain Layout -P2(...) +final case class P2[A](???) extends P[A] \end_layout -\end_inset +\begin_layout Plain Layout -, and -\begin_inset listings -inline true -status open +final case class P3[A](???) extends P[A] +\end_layout \begin_layout Plain Layout -P3(...) +final case class P4[A](???) extends P[A] \end_layout \end_inset -. - If we return +Each of the case classes represents one part of the disjunctive type. + Now we write the contents for each of the case classes, in order to implement + the data in each of the disjunctive parts: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -P1() +sealed trait P[A] \end_layout -\end_inset +\begin_layout Plain Layout -, we will lose the information about the value -\begin_inset listings -inline true -status open +final case class P1[A]() extends P[A] +\end_layout \begin_layout Plain Layout -x +final case class P2[A](x: A) extends P[A] \end_layout -\end_inset +\begin_layout Plain Layout -. - If we return -\begin_inset listings -inline true -status open +final case class P3[A](n: Int, x: A) extends P[A] +\end_layout \begin_layout Plain Layout -P3(...) +final case class P4[A](f: String => A) extends P[A] \end_layout \end_inset -, we will preserve the information about -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -x \end_layout +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-solvedExample-2a" + \end_inset - but lose the information -\begin_inset Index idx -status open -\begin_layout Plain Layout -information loss -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-solvedExample-2a" +plural "false" +caps "false" +noprefix "false" \end_inset - that the input value was a -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -P2 \end_layout -\end_inset - - rather than a +\begin_layout Standard +Find an equivalent disjunctive type for the type \begin_inset listings inline true status open \begin_layout Plain Layout -P3 +P = (Either[A, B], Either[C, D]) \end_layout \end_inset . - By returning -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -P2(...) \end_layout -\end_inset - - in that scope, we preserve the entire input information. - +\begin_layout Subparagraph +Solution \end_layout \begin_layout Standard -The value under +Begin by writing the given type in the type notation. + The tuple becomes the product type, and \begin_inset listings inline true status open \begin_layout Plain Layout -P2(...) +Either \end_layout \end_inset - must be of type -\begin_inset Formula $B$ + becomes the disjunctive (or +\begin_inset Quotes eld \end_inset -, and the only way of getting a value of type -\begin_inset Formula $B$ +sum +\begin_inset Quotes erd \end_inset - is to apply -\begin_inset Formula $f$ +) type: +\begin_inset Formula +\[ +P\triangleq(A+B)\times(C+D)\quad. +\] + \end_inset - to -\begin_inset Formula $x$ -\end_inset +By the usual rules of arithmetic, we expand brackets and obtain an equivalent + type: +\begin_inset Formula +\[ +P\cong A\times C+A\times D+B\times C+B\times D\quad. +\] -. - So, we return -\begin_inset listings -inline true -status open +\end_inset -\begin_layout Plain Layout +This is a disjunctive type having +\begin_inset Formula $4$ +\end_inset -P2(f(x)) + parts. \end_layout +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-solvedExample-3" + \end_inset -. + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-solvedExample-3" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + \end_layout \begin_layout Standard -Similarly, in the case -\begin_inset listings -inline true -status open +Show that the following type equivalences do +\emph on +not +\emph default + hold: +\begin_inset Formula $A+A\not\cong A$ +\end_inset -\begin_layout Plain Layout + and +\begin_inset Formula $A\times A\not\cong A$ +\end_inset -P3(x) +, although the corresponding logical identities hold. \end_layout +\begin_layout Subparagraph +Solution +\end_layout + +\begin_layout Standard +Note that the arithmetic equalities do not hold, +\begin_inset Formula $A+A\neq A$ \end_inset -, we should return + and +\begin_inset Formula $A\times A\ne A$ +\end_inset + +. + This already indicates that the types are not equivalent. + To build further intuition, consider that a value of type +\begin_inset Formula $A+A$ +\end_inset + + (in Scala, \begin_inset listings inline true status open \begin_layout Plain Layout -P3(f(x)) +Either[A, A] \end_layout \end_inset -. - The final code of +) is a \begin_inset listings inline true status open \begin_layout Plain Layout -map +Left(a) \end_layout \end_inset - is: + or a \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def map[A, B]: P[A] => (A => B) => P[B] = p => f => p match { +Right(a) \end_layout -\begin_layout Plain Layout +\end_inset - case P1() => P1() // No other choice here. -\end_layout + for some +\begin_inset listings +inline true +status open \begin_layout Plain Layout - case P2(x) => P2(f(x)) // Preserve information. +a:A \end_layout -\begin_layout Plain Layout +\end_inset - case P3(x) => P3(f(x)) // Preserve information. -\end_layout +. + In the code notation, it is either +\begin_inset Formula $a^{:A}+\bbnum 0$ +\end_inset -\begin_layout Plain Layout + or +\begin_inset Formula $\bbnum 0+a^{:A}$ +\end_inset -} -\end_layout +. + So, a value of type +\begin_inset Formula $A+A$ +\end_inset + contains a value of type +\begin_inset Formula $A$ \end_inset + with the additional information about whether it is the first or the second + part of the disjunctive type. + We cannot represent that information in a single value of type +\begin_inset Formula $A$ +\end_inset +. + \end_layout \begin_layout Standard -To verify the given law, we first write a matrix notation for -\begin_inset listings -inline true -status open +Similarly, a value of type +\begin_inset Formula $A\times A$ +\end_inset -\begin_layout Plain Layout + contains two (possibly different) values of type +\begin_inset Formula $A$ +\end_inset -map +, which cannot be represented by a single value of type +\begin_inset Formula $A$ +\end_inset + + without loss of information. \end_layout +\begin_layout Standard +However, the corresponding logical identities +\begin_inset Formula $\alpha\vee\alpha=\alpha$ +\end_inset + + and +\begin_inset Formula $\alpha\wedge\alpha=\alpha$ \end_inset -: + hold. + To see that, we could derive the four formulas: \begin_inset Formula \[ -\text{map}^{A,B}\triangleq p^{:\bbnum 1+A+A}\rightarrow f^{:A\rightarrow B}\rightarrow p\triangleright\,\begin{array}{|c||ccc|} - & \bbnum 1 & B & B\\ -\hline \bbnum 1 & \text{id} & \bbnum 0 & \bbnum 0\\ -A & \bbnum 0 & f & \bbnum 0\\ -A & \bbnum 0 & \bbnum 0 & f -\end{array}\quad. +\alpha\vee\alpha\Rightarrow\alpha\quad,\quad\quad\alpha\Rightarrow\alpha\vee\alpha\quad,\quad\quad\alpha\wedge\alpha\Rightarrow\alpha\quad,\quad\quad\alpha\Rightarrow\alpha\wedge\alpha\quad, \] \end_inset -The required law is written as an equation -\begin_inset Formula $\text{map}\left(p\right)(\text{id})=p$ +using the proof rules of Section +\begin_inset space ~ \end_inset -, called the -\begin_inset Index idx -status open -\begin_layout Plain Layout -identity laws!of -\family typewriter -map -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:The-rules-of-proof" +plural "false" +caps "false" +noprefix "false" \end_inset - -\series bold -identity law -\series default . - Substituting the code notation for + Alternatively, we may use the CH correspondence and show that the type + signatures: +\begin_inset Formula +\[ +\forall A.\,A+A\rightarrow A\quad,\quad\quad\forall A.\,A\rightarrow A+A\quad,\quad\quad\forall A.\,A\times A\rightarrow A\quad,\quad\quad\forall A.\,A\rightarrow A\times A\quad +\] + +\end_inset + +can be implemented via fully parametric functions. + For a programmer, it is easier to write code than to guess the correct + sequence of proof rules. + For the first pair of type signatures, we find: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -map +def f1[A]: Either[A, A] => A = { \end_layout -\end_inset - -, we verify the law: -\begin_inset Formula -\begin{align*} -\text{expect to equal }p:\quad & \text{map}\left(p\right)(\text{id})\\ -\text{apply \texttt{map()()} to arguments}:\quad & =p\triangleright\,\begin{array}{||ccc|} -\text{id} & \bbnum 0 & \bbnum 0\\ -\bbnum 0 & \text{id} & \bbnum 0\\ -\bbnum 0 & \bbnum 0 & \text{id} -\end{array}\\ -\text{identity function in matrix notation}:\quad & =p\triangleright\text{id}\\ -\triangleright\text{-notation}:\quad & =\text{id}\left(p\right)=p\quad. -\end{align*} +\begin_layout Plain Layout -\end_inset + case Left(a) => a // No other choice here. +\end_layout +\begin_layout Plain Layout + case Right(a) => a // No other choice here. \end_layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-solvedExample-8" +\begin_layout Plain Layout -\end_inset +} +\end_layout +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-solvedExample-8" -plural "false" -caps "false" -noprefix "false" +def f2[A]: A => Either[A, A] = { a => Left(a) } // Can be also Right(a). +\end_layout \end_inset - -\end_layout - -\begin_layout Standard -Implement +The presence of an arbitrary choice, to return \begin_inset listings inline true status open \begin_layout Plain Layout -map +Left(a) \end_layout \end_inset - and + or \begin_inset listings inline true status open \begin_layout Plain Layout -flatMap +Right(a) \end_layout \end_inset - for +, is a warning sign showing that additional information is required to create + a value of type \begin_inset listings inline true status open \begin_layout Plain Layout -Either[L, R] +Either[A, A] \end_layout \end_inset -, applied to the type parameter -\begin_inset listings -inline true -status open +. + This is precisely the information present in the type +\begin_inset Formula $A+A$ +\end_inset -\begin_layout Plain Layout + but missing in the type +\begin_inset Formula $A$ +\end_inset -L +. \end_layout +\begin_layout Standard +The code notation for these functions is: +\begin_inset Formula +\begin{align*} +f_{1} & \triangleq\,\begin{array}{|c||c|} + & A\\ +\hline A & a^{:A}\rightarrow a\\ +A & a^{:A}\rightarrow a +\end{array}\,=\,\begin{array}{|c||c|} + & A\\ +\hline A & \text{id}\\ +A & \text{id} +\end{array}\quad,\\ +f_{2} & \triangleq a^{:A}\rightarrow a+\bbnum 0^{:A}=\,\begin{array}{|c||cc|} + & A & A\\ +\hline A & a^{:A}\rightarrow a & \bbnum 0 +\end{array}\,=\,\begin{array}{|c||cc|} + & A & A\\ +\hline A & \text{id} & \bbnum 0 +\end{array}\quad. +\end{align*} + +\end_inset + +The composition of these functions is +\emph on +not +\emph default + equal to identity: +\begin_inset Formula +\[ +f_{1}\bef f_{2}=\,\begin{array}{||c|} +\text{id}\\ +\text{id} +\end{array}\,\bef\,\begin{array}{||cc|} +\text{id} & \bbnum 0\end{array}\,=\,\begin{array}{||cc|} +\text{id} & \bbnum 0\\ +\text{id} & \bbnum 0 +\end{array}\,\quad,\quad\text{while we have}\quad\text{id}^{:A+A\rightarrow A+A}=\,\begin{array}{||cc|} +\text{id} & \bbnum 0\\ +\bbnum 0 & \text{id} +\end{array}\quad. +\] + \end_inset -. -\end_layout -\begin_layout Subparagraph -Solution \end_layout \begin_layout Standard -For a type constructor, say, -\begin_inset Formula $P^{A}$ -\end_inset - -, the standard type signatures for +For the second pair of type signatures, the code is: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -map +def f1[A]: ((A, A)) => A = { case (a1, a2) => a1 } // Could be also `a2`. +\end_layout + +\begin_layout Plain Layout + +cef f2[A]: A => (A, A) = { a => (a, a) } // No other choice + here. \end_layout \end_inset - and +It is clear that the first function loses information when it returns \begin_inset listings inline true status open \begin_layout Plain Layout -flatMap +a1 \end_layout \end_inset - are: -\begin_inset Formula -\[ -\text{map}:P^{A}\rightarrow(A\rightarrow B)\rightarrow P^{B}\quad,\quad\quad\text{flatMap}:P^{A}\rightarrow(A\rightarrow P^{B})\rightarrow P^{B}\quad. -\] + and discards +\begin_inset listings +inline true +status open -\end_inset +\begin_layout Plain Layout + +a2 +\end_layout -If a type constructor has more than one type parameter, e.g., -\begin_inset Formula $P^{A,S,T}$ \end_inset -, one can define the functions + (or vice versa). +\end_layout + +\begin_layout Standard +The code notation for the functions \begin_inset listings inline true status open \begin_layout Plain Layout -map +f1 \end_layout \end_inset @@ -24291,506 +24351,441 @@ status open \begin_layout Plain Layout -flatMap +f2 \end_layout \end_inset - applied to a chosen type parameter. - For example, when applied to the type parameter -\begin_inset Formula $A$ + is: +\begin_inset Formula +\[ +f_{1}\triangleq a_{1}^{:A}\times a_{2}^{:A}\rightarrow a_{1}=\pi_{1}^{:A\times A\rightarrow A}\quad,\quad\quad f_{2}\triangleq a^{:A}\rightarrow a\times a=\Delta^{:A\rightarrow A\times A}\quad. +\] + \end_inset -, the type signatures are: +Computing the compositions of these functions, we find that +\begin_inset Formula $f_{2}\bef f_{1}=\text{id}$ +\end_inset + + while +\begin_inset Formula $f_{1}\bef f_{2}\ne\text{id}$ +\end_inset + +: \begin_inset Formula \begin{align*} -\text{map} & :P^{A,S,T}\rightarrow(A\rightarrow B)\rightarrow P^{B,S,T}\quad,\\ -\text{flatMap} & :P^{A,S,T}\rightarrow(A\rightarrow P^{B,S,T})\rightarrow P^{B,S,T}\quad. +f_{1}\bef f_{2} & =\left(a_{1}\times a_{2}\rightarrow a_{1}\right)\bef\left(a\rightarrow a\times a\right)\\ + & =\left(a_{1}\times a_{2}\rightarrow a_{1}\times a_{1}\right)\neq\text{id}=\left(a_{1}\times a_{2}\rightarrow a_{1}\times a_{2}\right)\quad. \end{align*} \end_inset -Being -\begin_inset Quotes eld -\end_inset -applied to the type parameter -\begin_inset Formula $A$ -\end_inset +\end_layout +\begin_layout Standard +We have implemented all four type signatures as fully parametric functions, + which shows that the corresponding logical formulas are all true (i.e., can + be derived using the proof rules). + However, the functions cannot be inverses of each other. + So, the type equivalences do not hold. +\end_layout + +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-solvedExample-4" -\begin_inset Quotes erd \end_inset - means that the other type parameters -\begin_inset Formula $S,T$ + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-solvedExample-4" +plural "false" +caps "false" +noprefix "false" + \end_inset - in -\begin_inset Formula $P^{A,S,T}$ + +\end_layout + +\begin_layout Standard +Show that +\begin_inset Formula $\left(\left(A\wedge B\right)\Rightarrow C\right)\neq(A\Rightarrow C)\vee(B\Rightarrow C)$ \end_inset - remain fixed while the type parameter -\begin_inset Formula $A$ + in the constructive logic, but the equality holds in Boolean logic. + This is another example where the Boolean reasoning fails to give correct + answers about implementability of type signatures. +\end_layout + +\begin_layout Subparagraph +Solution +\end_layout + +\begin_layout Standard +Begin by rewriting the logical equality as two implications: +\begin_inset Formula +\begin{align*} +(A\wedge B & \Rightarrow C)\Rightarrow(A\Rightarrow C)\vee(B\Rightarrow C)\\ +\quad\text{ and }\quad & \left((A\Rightarrow C)\vee(B\Rightarrow C)\right)\Rightarrow\left(\left(A\wedge B\right)\Rightarrow C\right)\quad. +\end{align*} + \end_inset - is replaced by -\begin_inset Formula $B$ +It is sufficient to show that one of these implications is incorrect. + Rather than looking for a proof tree in the constructive logic (which would + be difficult, since we need to demonstrate that +\emph on +no +\emph default + proof exists), let us use the CH correspondence. + According to the CH correspondence, an equivalent task is to implement + fully parametric functions with the type signatures: +\begin_inset Formula +\[ +(A\times B\rightarrow C)\rightarrow(A\rightarrow C)+(B\rightarrow C)\quad\text{ and }\quad(A\rightarrow C)+(B\rightarrow C)\rightarrow A\times B\rightarrow C\quad. +\] + \end_inset - in the type signatures of +For the first type signature, the Scala code is: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -map +def f1[A,B,C]: (((A, B)) => C) => Either[A => C, B => C] = { k => ??? } \end_layout \end_inset - and +We are required to return either a \begin_inset listings inline true status open \begin_layout Plain Layout -flatMap +Left(g) \end_layout \end_inset -. -\end_layout - -\begin_layout Standard -For the type + with \begin_inset listings inline true status open \begin_layout Plain Layout -Either[L, R] +g: A => C \end_layout \end_inset - (in the type notation, -\begin_inset Formula $L+R$ -\end_inset - -), we keep the type parameter -\begin_inset Formula $R$ -\end_inset - - fixed while -\begin_inset Formula $L$ -\end_inset +, or a +\begin_inset listings +inline true +status open - is replaced by -\begin_inset Formula $M$ -\end_inset +\begin_layout Plain Layout -. - So we obtain the type signatures: -\begin_inset Formula -\begin{align*} -\text{map} & :L+R\rightarrow(L\rightarrow M)\rightarrow M+R\quad,\\ -\text{flatMap} & :L+R\rightarrow(L\rightarrow M+R)\rightarrow M+R\quad. -\end{align*} +Right(h) +\end_layout \end_inset -Implementing these functions is straightforward: + with \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def map[L,M,R]: Either[L, R] => (L => M) => Either[M, R] = e => f => e match - { +h: B => C \end_layout -\begin_layout Plain Layout - - case Left(x) => Left(f(x)) -\end_layout +\end_inset -\begin_layout Plain Layout - - case Right(y) => Right(y) -\end_layout +. + The only given data is a function +\begin_inset listings +inline true +status open \begin_layout Plain Layout -} +k \end_layout -\begin_layout Plain Layout - -\end_layout +\end_inset -\begin_layout Plain Layout + of type +\begin_inset Formula $A\times B\rightarrow C$ +\end_inset -def flatMap[L,M,R]: Either[L, R] => (L => Either[M, R]) => Either[M, R] - = e => f => e match { -\end_layout +, so the decision of whether to return a +\begin_inset listings +inline true +status open \begin_layout Plain Layout - case Left(x) => f(x) +Left \end_layout -\begin_layout Plain Layout +\end_inset - case Right(y) => Right(y) -\end_layout + or a +\begin_inset listings +inline true +status open \begin_layout Plain Layout -} +Right \end_layout \end_inset -The code notation for these functions is: -\begin_inset Formula -\begin{align*} -\text{map} & \triangleq e^{:L+R}\rightarrow f^{:L\rightarrow M}\rightarrow e\triangleright\,\begin{array}{|c||cc|} - & M & R\\ -\hline L & f & \bbnum 0\\ -R & \bbnum 0 & \text{id} -\end{array}\quad,\\ -\text{flatMap} & \triangleq e^{:L+R}\rightarrow f^{:L\rightarrow M+R}\rightarrow e\triangleright\,\begin{array}{|c||c|} - & M+R\\ -\hline L & f\\ -R & y^{:R}\rightarrow\bbnum 0^{:M}+y -\end{array}\quad. -\end{align*} - -\end_inset - -Note that the code matrix for + must be hard-coded in the function \begin_inset listings inline true status open \begin_layout Plain Layout -flatMap +f1 \end_layout \end_inset - cannot be split into the -\begin_inset Formula $M$ -\end_inset - - and -\begin_inset Formula $R$ -\end_inset - - columns because we do not know in advance which part of the disjunctive - type -\begin_inset Formula $M+R$ -\end_inset + independently of +\begin_inset listings +inline true +status open - will be returned when we evaluate -\begin_inset Formula $f(x^{:L})$ -\end_inset +\begin_layout Plain Layout -. +k \end_layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-solvedExample-9" - \end_inset +. + Can we produce a function +\begin_inset listings +inline true +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-solvedExample-9" -plural "false" -caps "false" -noprefix "false" - -\end_inset +\begin_layout Plain Layout -* +g \end_layout -\begin_layout Standard -Define a type constructor -\begin_inset Formula $\text{State}^{S,A}\equiv S\rightarrow A\times S$ \end_inset - and implement the functions: -\end_layout - -\begin_layout Standard - -\series bold -(a) -\series default - -\begin_inset Formula $\text{pure}^{S,A}:A\rightarrow\text{State}^{S,A}\quad.$ -\end_inset + of type +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +A => C \end_layout -\begin_layout Standard - -\series bold -(b) -\series default - -\begin_inset Formula $\text{map}^{S,A,B}:\text{State}^{S,A}\rightarrow(A\rightarrow B)\rightarrow\text{State}^{S,B}\quad.$ \end_inset +? Given a value of type +\begin_inset listings +inline true +status open -\end_layout +\begin_layout Plain Layout -\begin_layout Standard +A +\end_layout -\series bold -(c) -\series default - -\begin_inset Formula $\text{flatMap}^{S,A,B}:\text{State}^{S,A}\rightarrow(A\rightarrow\text{State}^{S,B})\rightarrow\text{State}^{S,B}\quad.$ \end_inset +, we would need to return a value of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +C \end_layout -\begin_layout Subparagraph -Solution -\end_layout +\end_inset -\begin_layout Standard -It is assumed that all functions must be fully parametric and preserve as - much information as possible. - We define the type alias: +. + The only way to obtain a value of type \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -type State[S, A] = S => (A, S) +C \end_layout \end_inset + is by applying +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +k \end_layout -\begin_layout Standard - -\series bold -(a) -\series default - The type signature is -\begin_inset Formula $A\rightarrow S\rightarrow A\times S$ \end_inset -, and there is only one implementation: + to some arguments. + But to apply \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def pure[S, A]: A => State[S, A] = a => s => (a, s) +k \end_layout \end_inset -In the code notation, this is written as: -\begin_inset Formula -\[ -\text{pu}^{S,A}\triangleq a^{:A}\rightarrow s^{:S}\rightarrow a\times s\quad. -\] - -\end_inset +, we need a value of type +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +B \end_layout -\begin_layout Standard - -\series bold -(b) -\series default - The type signature is: -\begin_inset Formula -\[ -\text{map}^{S,A,B}:(S\rightarrow A\times S)\rightarrow(A\rightarrow B)\rightarrow S\rightarrow B\times S\quad. -\] - \end_inset -Begin writing a Scala implementation: +, which we do not have. + So we cannot produce a \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def map[S, A, B]: State[S, A] => (A => B) => State[S, B] = { t => f => s - => ??? } +g: A => C \end_layout \end_inset -We need to compute a value of -\begin_inset Formula $B\times S$ -\end_inset +. + Similarly, we cannot produce a function +\begin_inset listings +inline true +status open - from the curried arguments -\begin_inset Formula $t^{:S\rightarrow A\times S}$ -\end_inset +\begin_layout Plain Layout -, -\begin_inset Formula $f^{:A\rightarrow B}$ -\end_inset +h +\end_layout -, and -\begin_inset Formula $s^{:S}$ \end_inset -. - We begin writing the code of + of type \begin_inset listings inline true status open \begin_layout Plain Layout -map +B => C \end_layout \end_inset - using a typed hole: -\begin_inset Formula -\[ -\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow\text{???}^{:B}\times\text{???}^{:S}\quad. -\] +. +\end_layout +\begin_layout Standard +We repeat the same argument in the type notation. + Obtaining a value of type +\begin_inset Formula $(A\rightarrow C)+(B\rightarrow C)$ \end_inset -The only way of getting a value of type -\begin_inset Formula $B$ + means to compute either +\begin_inset Formula $g^{:A\rightarrow C}+\bbnum 0$ \end_inset - is by applying -\begin_inset Formula $f$ -\end_inset - - to a value of type -\begin_inset Formula $A$ -\end_inset - -: -\begin_inset Formula -\[ -\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow f(\text{???}^{:A})\times\text{???}^{:S}\quad. -\] - -\end_inset - -The only possibility of filling the typed hole -\begin_inset Formula $\text{???}^{:A}$ -\end_inset - - is to apply -\begin_inset Formula $t$ -\end_inset - - to a value of type -\begin_inset Formula $S$ + or +\begin_inset Formula $\bbnum 0+h^{:B\rightarrow C}$ \end_inset . - We already have such a value, -\begin_inset Formula $s^{:S}$ + This decision must be hard-coded since the only data is a function +\begin_inset Formula $k^{:A\times B\rightarrow C}$ \end_inset . - Computing -\begin_inset Formula $t(s)$ -\end_inset - - yields a pair of type -\begin_inset Formula $A\times S$ + We can compute +\begin_inset Formula $g^{:A\rightarrow C}$ \end_inset -, from which we may take the first part (of type -\begin_inset Formula $A$ + only by partially applying +\begin_inset Formula $k^{:A\times B\rightarrow C}$ \end_inset -) to fill the typed hole -\begin_inset Formula $\text{???}^{:A}$ + to a value of type +\begin_inset Formula $B$ \end_inset . - The second part of the pair is a value of type -\begin_inset Formula $S$ + However, we have no values of type +\begin_inset Formula $B$ \end_inset - that we may use to fill the second typed hole, -\begin_inset Formula $\text{???}^{:S}$ +. + Similarly, we cannot compute a value +\begin_inset Formula $h^{:B\rightarrow C}$ \end_inset . - So, the Scala code is: \end_layout \begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "62col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - +The inverse type signature +\emph on +can +\emph default + be implemented: \begin_inset listings -lstparams "numbers=left" inline false status open \begin_layout Plain Layout -def map[S, A, B]: State[S, A] => (A => B) => State[S, B] = { -\end_layout - -\begin_layout Plain Layout - - t => f => s => +def f2[A,B,C]: Either[A=>C, B=>C] => ((A,B)) => C = { \end_layout \begin_layout Plain Layout - val (a, s2) = t(s) + case Left(g) => { case (a, b) => g(a) } \end_layout \begin_layout Plain Layout - (f(a), s2) // We could also return `(f(a), s)` here. + case Right(h) => { case (a, b) => h(b) } \end_layout \begin_layout Plain Layout @@ -24801,11 +24796,14 @@ def map[S, A, B]: State[S, A] => (A => B) => State[S, B] = { \end_inset -\begin_inset VSpace -125baselineskip% -\end_inset - - -\end_layout +\begin_inset Formula +\[ +f_{2}\triangleq\,\begin{array}{|c||c|} + & A\times B\rightarrow C\\ +\hline A\rightarrow C & g^{:A\rightarrow C}\rightarrow a\times b\rightarrow g(a)\\ +B\rightarrow C & h^{:B\rightarrow C}\rightarrow a\times b\rightarrow h(b) +\end{array}\quad. +\] \end_inset @@ -24813,313 +24811,305 @@ def map[S, A, B]: State[S, A] => (A => B) => State[S, B] = { \end_layout \begin_layout Standard -\noindent -Why not return the original value -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -s -\end_layout +Let us now show that the logical identity: +\begin_inset Formula +\begin{equation} +((\alpha\wedge\beta)\Rightarrow\gamma)=((\alpha\Rightarrow\gamma)\vee(\beta\Rightarrow\gamma))\label{eq:ch-example-identity-boolean-not-constructive} +\end{equation} \end_inset - in the tuple -\begin_inset Formula $B\times S$ +holds in Boolean logic. + A straightforward calculation is to simplify the Boolean expression using + Eq. +\begin_inset space ~ \end_inset -, instead of the new value -\begin_inset listings -inline true -status open +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-definition-of-implication-in-Boolean-logic" +plural "false" +caps "false" +noprefix "false" -\begin_layout Plain Layout +\end_inset -s2 -\end_layout +), which only holds in Boolean logic (but not in the constructive logic). + We find: +\begin_inset Formula +\begin{align*} +\text{left-hand side of Eq.~(\ref{eq:ch-example-identity-boolean-not-constructive})}:\quad & \left(\alpha\wedge\beta\right)\gunderline{\Rightarrow}\,\gamma\\ +\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & \quad=\gunderline{\neg(\alpha\wedge\beta)}\vee\gamma\\ +\text{use de Morgan's law}:\quad & \quad=\neg\alpha\vee\neg\beta\vee\gamma\quad.\\ +\text{right-hand side of Eq.~(\ref{eq:ch-example-identity-boolean-not-constructive})}:\quad & (\gunderline{\alpha\Rightarrow\gamma})\vee(\gunderline{\beta\Rightarrow\gamma})\\ +\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & \quad=\neg\alpha\vee\gunderline{\gamma}\vee\neg\beta\vee\gunderline{\gamma}\\ +\text{use identity }\gamma\vee\gamma=\gamma:\quad & \quad=\neg\alpha\vee\neg\beta\vee\gamma\quad. +\end{align*} \end_inset -? The reason is that we would like to preserve information as much as possible. - If we return -\begin_inset listings -inline true -status open +Both sides of Eq. +\begin_inset space ~ +\end_inset -\begin_layout Plain Layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-identity-boolean-not-constructive" +plural "false" +caps "false" +noprefix "false" -(f(a), s) -\end_layout +\end_inset +) are equal to the same formula, +\begin_inset Formula $\neg\alpha\vee\neg\beta\vee\gamma$ \end_inset - in line 4, we will have discarded the computed value -\begin_inset listings -inline true -status open +, so the identity holds. +\end_layout -\begin_layout Plain Layout +\begin_layout Standard +This calculation does not work in the constructive logic because its proof + rules can derive neither the Boolean formula +\begin_inset space ~ +\end_inset -s2 -\end_layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-definition-of-implication-in-Boolean-logic" +plural "false" +caps "false" +noprefix "false" \end_inset -, which is a loss of information. -\end_layout +) nor the +\series bold +law of de Morgan +\series default -\begin_layout Standard -To write the code notation for -\begin_inset listings -inline true +\begin_inset Index idx status open \begin_layout Plain Layout - -map +law of de Morgan \end_layout \end_inset -, we need to destructure the pair that -\begin_inset Formula $t(s)$ +, +\begin_inset Formula $\neg(\alpha\wedge\beta)=\left(\neg\alpha\vee\neg\beta\right)$ \end_inset - returns. - We can write explicit destructuring code like this: -\begin_inset Formula -\[ -\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow(a^{:A}\times s_{2}^{:S}\rightarrow f(a)\times s_{2})(t(s))\quad. -\] +. +\end_layout +\begin_layout Standard +Another way of proving the Boolean identity +\begin_inset space ~ \end_inset -If we temporarily denote by -\begin_inset Formula $q$ -\end_inset +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-identity-boolean-not-constructive" +plural "false" +caps "false" +noprefix "false" - the following destructuring function: -\begin_inset Formula -\[ -q\triangleq(a^{:A}\times s_{2}^{:S}\rightarrow f(a)\times s_{2})\quad, -\] +\end_inset +) is to enumerate all possible truth values for the variables +\begin_inset Formula $\alpha$ \end_inset -we will notice that the expression -\begin_inset Formula $s\rightarrow q(t(s))$ +, +\begin_inset Formula $\beta$ \end_inset - is a function composition applied to -\begin_inset Formula $s$ +, and +\begin_inset Formula $\gamma$ \end_inset . - So, we rewrite -\begin_inset Formula $s\rightarrow q(t(s))$ + The left-hand side, +\begin_inset Formula $\left(\alpha\wedge\beta\right)\Rightarrow\gamma$ \end_inset - as the composition -\begin_inset Formula $t\bef q$ +, can be +\begin_inset Formula $False$ \end_inset - and obtain shorter code: -\begin_inset Formula -\[ -\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow t\bef(a^{:A}\times s^{:S}\rightarrow f(a)\times s)\quad. -\] + only if +\begin_inset Formula $\alpha\wedge\beta=True$ +\end_inset + (that is, both +\begin_inset Formula $\alpha$ \end_inset -Shorter formulas are often easier to reason about in derivations, although - not necessarily easier to read when converted to program code. -\end_layout - -\begin_layout Standard - -\series bold -(c) -\series default - The required type signature is: -\begin_inset Formula -\[ -\text{flatMap}^{S,A,B}:(S\rightarrow A\times S)\rightarrow(A\rightarrow S\rightarrow B\times S)\rightarrow S\rightarrow B\times S\quad. -\] - + and +\begin_inset Formula $\beta$ \end_inset -We perform code reasoning with typed holes: -\begin_inset Formula -\[ -\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow\text{???}^{:B\times S}\quad. -\] - + are +\begin_inset Formula $True$ \end_inset -To fill -\begin_inset Formula $\text{???}^{:B\times S}$ +) and +\begin_inset Formula $\gamma=False$ \end_inset -, we need to apply -\begin_inset Formula $f$ +. + For all other truth values of +\begin_inset Formula $\alpha$ \end_inset - to some arguments, since -\begin_inset Formula $f$ +, +\begin_inset Formula $\beta$ \end_inset - is the only function that returns any values of type -\begin_inset Formula $B$ +, and +\begin_inset Formula $\gamma$ \end_inset -. - Applying -\begin_inset Formula $f$ +, the formula +\begin_inset Formula $\left(\alpha\wedge\beta\right)\Rightarrow\gamma$ \end_inset - to two values will yield a value of type -\begin_inset Formula $B\times S$ + is +\begin_inset Formula $True$ \end_inset -, just as we need: -\begin_inset Formula -\[ -\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow f(\text{???}^{:A})(\text{???}^{:S})\quad. -\] - +. + Let us determine when the right-hand side, +\begin_inset Formula $(\alpha\Rightarrow\gamma)\vee(\beta\Rightarrow\gamma)$ \end_inset -To fill the new typed holes, we need to apply -\begin_inset Formula $t$ +, can be +\begin_inset Formula $False$ \end_inset - to an argument of type -\begin_inset Formula $S$ +. + This can happen only if both parts of the disjunction are +\begin_inset Formula $False$ \end_inset . - We have only one given value -\begin_inset Formula $s^{:S}$ + That means +\begin_inset Formula $\alpha=True$ \end_inset - of type -\begin_inset Formula $S$ +, +\begin_inset Formula $\beta=True$ \end_inset -, so we must compute -\begin_inset Formula $t(s)$ +, and +\begin_inset Formula $\gamma=False$ \end_inset - and destructure it: -\begin_inset Formula -\[ -\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow\left(a\times s_{2}\rightarrow f(a)(s_{2})\right)(t(s))\quad. -\] - +. + So, the two sides of the identity +\begin_inset space ~ \end_inset -Translating this notation into Scala code, we obtain: -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -def flatMap[S, A, B]: State[S, A] => (A => State[S, B]) => State[S, B] = - { -\end_layout - -\begin_layout Plain Layout - - t => f => s => -\end_layout - -\begin_layout Plain Layout - - val (a, s2) = t(s) -\end_layout - -\begin_layout Plain Layout - - f(a)(s2) // We could also return `f(a)(s)` here, but that - would lose information. -\end_layout - -\begin_layout Plain Layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-identity-boolean-not-constructive" +plural "false" +caps "false" +noprefix "false" -} -\end_layout +\end_inset +) are both +\begin_inset Formula $True$ \end_inset -In order to preserve information, we choose not to discard the computed - value -\begin_inset listings -inline true -status open + or both +\begin_inset Formula $False$ +\end_inset -\begin_layout Plain Layout + with any choice of truth values of +\begin_inset Formula $\alpha$ +\end_inset -s2 -\end_layout +, +\begin_inset Formula $\beta$ +\end_inset +, and +\begin_inset Formula $\gamma$ \end_inset . -\end_layout - -\begin_layout Standard -The code notation for this -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout + In Boolean logic, this is sufficient to prove the identity +\begin_inset space ~ +\end_inset -flatMap -\end_layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-identity-boolean-not-constructive" +plural "false" +caps "false" +noprefix "false" \end_inset - can be simplified to: -\begin_inset Formula -\[ -\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow t\bef\left(a\times s\rightarrow f(a)(s)\right)\quad. -\] - +). + +\begin_inset Formula $\square$ \end_inset \end_layout -\begin_layout Subsection -Exercises -\begin_inset Index idx -status open +\begin_layout Standard +The following example shows how to use the formulas from Tables +\begin_inset space ~ +\end_inset -\begin_layout Plain Layout -exercises -\end_layout + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Logical-identities-with-disjunction-and-conjunction" +plural "false" +caps "false" +noprefix "false" \end_inset +– +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Logical-identities-with-function-types" +plural "false" +caps "false" +noprefix "false" + +\end_inset + to derive the type equivalence of complicated type expressions without + need for proofs. \end_layout \begin_layout Subsubsection -Exercise +Example \begin_inset CommandInset label LatexCommand label -name "subsec:ch-Exercise-0" +name "subsec:ch-solvedExample-5-2" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-Exercise-0" +reference "subsec:ch-solvedExample-5-2" plural "false" caps "false" noprefix "false" @@ -25130,79 +25120,62 @@ noprefix "false" \end_layout \begin_layout Standard -Find the cardinality of the type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -P = Option[Boolean => Option[Boolean]] +Use known formulas to verify the type equivalences without direct proofs: \end_layout +\begin_layout Standard + +\series bold +(a) +\series default + +\begin_inset Formula $A\times\left(A+\bbnum 1\right)\times\left(A+\bbnum 1+\bbnum 1\right)\cong A\times\left(\bbnum 1+\bbnum 1+A\times\left(\bbnum 1+\bbnum 1+\bbnum 1+A\right)\right)$ \end_inset . - Show that -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -P \end_layout -\end_inset +\begin_layout Standard - is equivalent to -\begin_inset listings -inline true -status open +\series bold +(b) +\series default + +\begin_inset Formula $\bbnum 1+A+B\rightarrow\bbnum 1\times B\cong\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B$ +\end_inset -\begin_layout Plain Layout +. +\end_layout -Option[Boolean] => Boolean +\begin_layout Subparagraph +Solution \end_layout -\end_inset +\begin_layout Standard -, and that the equivalence is accidental -\begin_inset Index idx -status open - -\begin_layout Plain Layout -type equivalence!accidental -\end_layout - -\end_inset - - and not -\begin_inset Quotes eld -\end_inset +\series bold +(a) +\series default + We can expand brackets in type expressions as in arithmetic: +\begin_inset Formula +\begin{align*} +A\times\left(A+\bbnum 1\right) & \cong A\times A+A\times\bbnum 1\cong A\times A+A\quad,\\ +A\times\left(A+\bbnum 1\right)\times\left(A+\bbnum 1+\bbnum 1\right) & \cong\left(A\times A+A\right)\times\left(A+\bbnum 1+\bbnum 1\right)\\ + & \cong A\times A\times A+A\times A+A\times A\times\left(\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\\ + & \cong A\times A\times A+A\times A\times\left(\bbnum 1+\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\quad. +\end{align*} -natural -\begin_inset Quotes erd \end_inset -. -\end_layout - -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-1-a" - +The result looks like a polynomial in +\begin_inset Formula $A$ \end_inset - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-1-a" -plural "false" -caps "false" -noprefix "false" +, which we can now rearrange into the required form: +\begin_inset Formula +\[ +A\times A\times A+A\times A\times\left(\bbnum 1+\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\cong A\times\left(\bbnum 1+\bbnum 1+A\times\left(\bbnum 1+\bbnum 1+\bbnum 1+A\right)\right)\quad. +\] \end_inset @@ -25210,74 +25183,90 @@ noprefix "false" \end_layout \begin_layout Standard -Verify the type equivalences -\begin_inset Formula $A+A\cong\bbnum 2\times A$ -\end_inset - and -\begin_inset Formula $A\times A\cong\bbnum 2\rightarrow A$ +\series bold +(b) +\series default + Keep in mind that the conventions of the type notation make the function + arrow +\begin_inset Formula $\left(\rightarrow\right)$ \end_inset -, where -\begin_inset Formula $\bbnum 2$ + group weaker than other type operations. + So, the type expression +\begin_inset Formula $\bbnum 1+A+B\rightarrow\bbnum 1\times B$ \end_inset - denotes the -\begin_inset listings -inline true -status open + means a function from +\begin_inset Formula $\bbnum 1+A+B$ +\end_inset -\begin_layout Plain Layout + to +\begin_inset Formula $\bbnum 1\times B$ +\end_inset -Boolean +. + \end_layout +\begin_layout Standard +Begin by using the equivalence +\begin_inset Formula $\bbnum 1\times B\cong B$ \end_inset - type. -\end_layout + to obtain +\begin_inset Formula $\bbnum 1+A+B\rightarrow B$ +\end_inset -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-1" +. + Now we use another rule: +\begin_inset Formula +\[ +A+B\rightarrow C\cong\left(A\rightarrow C\right)\times\left(B\rightarrow C\right) +\] \end_inset +and derive the equivalence: +\begin_inset Formula +\[ +\bbnum 1+A+B\rightarrow B\cong\left(\bbnum 1\rightarrow B\right)\times\left(A\rightarrow B\right)\times\left(B\rightarrow B\right)\quad. +\] -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-1" -plural "false" -caps "false" -noprefix "false" +\end_inset +Finally, we note that +\begin_inset Formula $\bbnum 1\rightarrow B\cong B$ \end_inset + and that the type product is commutative, so we can rearrange the last + type expression into the required form: +\begin_inset Formula +\[ +B\times\left(A\rightarrow B\right)\times\left(B\rightarrow B\right)\cong\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B\quad. +\] -\end_layout +\end_inset -\begin_layout Standard -Show that -\begin_inset Formula $A\Rightarrow(B\vee C)\neq(A\Rightarrow B)\wedge(A\Rightarrow C)$ +We obtain the required type expression: +\begin_inset Formula $\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B$ \end_inset - in constructive and Boolean logic. +. \end_layout \begin_layout Subsubsection -Exercise +Example \begin_inset CommandInset label LatexCommand label -name "subsec:ch-solvedExample-5-1" +name "subsec:ch-solvedExample-5" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-solvedExample-5-1" +reference "subsec:ch-solvedExample-5" plural "false" caps "false" noprefix "false" @@ -25288,344 +25277,337 @@ noprefix "false" \end_layout \begin_layout Standard -Verify the type equivalence -\begin_inset Formula $\left(A\rightarrow B\times C\right)\cong\left(A\rightarrow B\right)\times\left(A\rightarrow C\right)$ +Denote +\begin_inset Formula $\text{Read}^{E,A}\triangleq E\rightarrow A$ \end_inset - with full proofs. -\end_layout - -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-type-identity-4" - + and implement fully parametric functions with types +\begin_inset Formula $A\rightarrow\text{Read}^{E,A}$ \end_inset - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-type-identity-4" -plural "false" -caps "false" -noprefix "false" - + and +\begin_inset Formula $\text{Read}^{E,A}\rightarrow(A\rightarrow B)\rightarrow\text{Read}^{E,B}$ \end_inset - +. \end_layout -\begin_layout Standard -Use known rules to verify the type equivalences without need for proofs: +\begin_layout Subparagraph +Solution \end_layout \begin_layout Standard - -\series bold -(a) -\series default - -\begin_inset Formula $\left(A+B\right)\times\left(A\rightarrow B\right)\cong A\times\left(A\rightarrow B\right)+\left(\bbnum 1+A\rightarrow B\right)\quad.$ +Begin by defining a type alias for the type constructor +\begin_inset Formula $\text{Read}^{E,A}$ \end_inset +: +\begin_inset listings +inline false +status open -\end_layout +\begin_layout Plain Layout -\begin_layout Standard +type Read[E, A] = E => A +\end_layout -\series bold -(b) -\series default - -\begin_inset Formula $\left(A\times(\bbnum 1+A)\rightarrow B\right)\cong\left(A\rightarrow B\right)\times\left(A\rightarrow A\rightarrow B\right)\quad.$ \end_inset +The first type signature has only one implementation: +\begin_inset listings +inline false +status open +\begin_layout Plain Layout + +def p[E, A]: A => Read[E, A] = { x => _ => x } \end_layout -\begin_layout Standard +\end_inset -\series bold -(c) -\series default - -\begin_inset Formula $A\rightarrow\left(\bbnum 1+B\right)\rightarrow C\times D\cong\left(A\rightarrow C\right)\times\left(A\rightarrow D\right)\times\left(A\times B\rightarrow C\right)\times\left(A\times B\rightarrow D\right)\quad.$ +We +\emph on +must +\emph default + discard the argument of type +\begin_inset Formula $E$ \end_inset +; we cannot use it for computing a value of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +A \end_layout -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-2" - \end_inset + given +\begin_inset listings +inline true +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-2" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout -\end_inset +x:A +\end_layout +\end_inset +. \end_layout \begin_layout Standard -Write the type notation for +The second type signature has three type parameters. + It is the curried version of the function \begin_inset listings inline true status open \begin_layout Plain Layout -Either[(A, Int), Either[(A, Char), (A, Float)]] +map \end_layout \end_inset -. - Transform this type into an equivalent type of the form -\begin_inset Formula $A\times(...)$ -\end_inset +: +\begin_inset listings +inline false +status open -. -\end_layout +\begin_layout Plain Layout -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-3" +def map[E, A, B]: Read[E, A] => (A => B) => Read[E, B] = ??? +\end_layout \end_inset +Expanding the type alias, we see that the two curried arguments are functions + of types +\begin_inset Formula $E\rightarrow A$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-3" -plural "false" -caps "false" -noprefix "false" + and +\begin_inset Formula $A\rightarrow B$ +\end_inset +. + The forward composition of these functions is a function of type +\begin_inset Formula $E\rightarrow B$ \end_inset +, or +\begin_inset Formula $\text{Read}^{E,B}$ +\end_inset +, which is exactly what we are required to return. + So, the code can be written as: \end_layout \begin_layout Standard -Define a type -\begin_inset Formula $\text{OptE}^{T,A}\triangleq\bbnum 1+T+A$ -\end_inset - - and implement information-preserving \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -map +def map[E, A, B]: (E => A) => (A => B) => E => B = { r => f => r andThen + f } \end_layout \end_inset - and -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -flatMap -\end_layout - +If we did not notice this shortcut, we would reason differently: We are + required to compute a value of type +\begin_inset Formula $B$ \end_inset - for it, applied to the type parameter -\begin_inset Formula $A$ + given +\emph on +three +\emph default + curried arguments +\begin_inset Formula $r^{:E\rightarrow A}$ \end_inset -. - Get the same result using the equivalent type -\begin_inset Formula $(\bbnum 1+A)+T$ +, +\begin_inset Formula $f^{:A\rightarrow B}$ \end_inset -, i.e., -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Either[Option[A], T] -\end_layout - +, and +\begin_inset Formula $e^{:E}$ \end_inset . - The required type signatures are: + Write this requirement as: \begin_inset Formula -\begin{align*} -\text{map}^{A,B,T} & :\text{OptE}^{T,A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{OptE}^{T,B}\quad,\\ -\text{flatMap}^{A,B,T} & :\text{OptE}^{T,A}\rightarrow(A\rightarrow\text{OptE}^{T,B})\rightarrow\text{OptE}^{T,B}\quad. -\end{align*} +\[ +\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow???^{:B}\quad, +\] \end_inset - -\end_layout - -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-4" - +The symbol +\begin_inset Formula $\text{???}^{:B}$ \end_inset + is called a +\begin_inset Index idx +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-4" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout +typed hole +\end_layout \end_inset -\end_layout - -\begin_layout Standard -Implement the -\begin_inset listings -inline true +\series bold +typed hole +\series default +. + It stands for a value that we are still figuring out how to compute, but + whose type is already known. + Typed holes are supported in Scala by an experimental compiler plugin. +\begin_inset Foot status open \begin_layout Plain Layout +See +\family typewriter -map -\end_layout +\begin_inset CommandInset href +LatexCommand href +name "https://github.com/cb372/scala-typed-holes" +target "https://github.com/cb372/scala-typed-holes" +literal "false" \end_inset - function for the type constructor -\begin_inset listings -inline true -status open -\begin_layout Plain Layout +\end_layout -P[A] +\end_inset + + The plugin will print the known information about the typed hole. \end_layout +\begin_layout Standard +To fill the typed hole +\begin_inset Formula $\text{???}^{:B}$ \end_inset - from Example -\begin_inset space ~ +, we need a value of type +\begin_inset Formula $B$ \end_inset +. + Since no arguments have type +\begin_inset Formula $B$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-solvedExample-2" -plural "false" -caps "false" -noprefix "false" +, the only way of getting a value of type +\begin_inset Formula $B$ +\end_inset + is to apply +\begin_inset Formula $f^{:A\rightarrow B}$ \end_inset -. - The required type signature is -\begin_inset Formula $P^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow P^{B}$ + to some value of type +\begin_inset Formula $A$ \end_inset . - Preserve information as much as possible. -\end_layout - -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-4-1" + So we write: +\begin_inset Formula +\[ +\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(???^{:A})\quad. +\] \end_inset +The only way of getting an +\begin_inset Formula $A$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-4-1" -plural "false" -caps "false" -noprefix "false" - + is to apply +\begin_inset Formula $r$ \end_inset + to some value of type +\begin_inset Formula $E$ +\end_inset -\end_layout +: +\begin_inset Formula +\[ +\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(r(???^{:E}))\quad. +\] -\begin_layout Standard -For the type constructor -\begin_inset Formula $Q^{T,A}$ \end_inset - defined in Exercise -\begin_inset space ~ +We have exactly one value of type +\begin_inset Formula $E$ \end_inset +, namely +\begin_inset Formula $e^{:E}$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Exercise-type-notation-1" -plural "false" -caps "false" -noprefix "false" +. + So, the code must be: +\begin_inset Formula +\[ +\text{map}^{E,A,B}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(r(e))\quad. +\] \end_inset -, define the +Translate this to the Scala syntax: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -map +def map[E, A, B]: (E => A) => (A => B) => E => B = { r => f => e => f(r(e)) + } \end_layout \end_inset - function, preserving information as much as possible: -\begin_inset Formula -\[ -\text{map}^{T,A,B}:Q^{T,A}\rightarrow\left(A\rightarrow B\right)\rightarrow Q^{T,B}\quad. -\] +We may now notice that the expression +\begin_inset Formula $e\rightarrow f(r(e))$ +\end_inset + is a function composition +\begin_inset Formula $r\bef f$ \end_inset + applied to +\begin_inset Formula $e$ +\end_inset +, and simplify the code accordingly. \end_layout \begin_layout Subsubsection -Exercise +Example \begin_inset CommandInset label LatexCommand label -name "subsec:Exercise-disjunctive-6" +name "subsec:ch-solvedExample-6" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Exercise-disjunctive-6" +reference "subsec:ch-solvedExample-6" plural "false" caps "false" noprefix "false" @@ -25636,1300 +25618,1057 @@ noprefix "false" \end_layout \begin_layout Standard -Define a recursive type constructor -\begin_inset Formula $\text{Tr}_{3}$ -\end_inset - - as -\begin_inset Formula $\text{Tr}_{3}{}^{A}\triangleq\bbnum 1+A\times A\times A\times\text{Tr}_{3}{}^{A}$ -\end_inset - - and implement the +Show that the type signature \begin_inset listings inline true status open \begin_layout Plain Layout -map +Read[A, T] => (A => B) => Read[B, T] \end_layout \end_inset - function for it, with the standard type signature: -\begin_inset Formula $\text{map}^{A,B}:\text{Tr}_{3}{}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Tr}_{3}{}^{B}\quad.$ -\end_inset + cannot be implemented as a fully parametric function. +\end_layout + +\begin_layout Subparagraph +Solution +\end_layout + +\begin_layout Standard +Expand the type signature and try implementing this function: +\begin_inset listings +inline false +status open +\begin_layout Plain Layout +def m[A, B, T] : (A => T) => (A => B) => B => T = { r => f => b => ??? } \end_layout -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-5" +\end_inset +Given values +\begin_inset Formula $r^{:A\rightarrow T}$ \end_inset +, +\begin_inset Formula $f^{:A\rightarrow B}$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-5" -plural "false" -caps "false" -noprefix "false" +, and +\begin_inset Formula $b^{:B}$ +\end_inset +, we need to compute a value of type +\begin_inset Formula $T$ \end_inset +: +\begin_inset Formula +\[ +m=r^{:A\rightarrow T}\rightarrow f^{:A\rightarrow B}\rightarrow b^{:B}\rightarrow???^{:T}\quad. +\] -\end_layout +\end_inset -\begin_layout Standard -Implement fully parametric, information-preserving functions with the types: -\end_layout +The only way of getting a value of type +\begin_inset Formula $T$ +\end_inset -\begin_layout Standard + is to apply +\begin_inset Formula $r$ +\end_inset -\series bold -(a) -\series default - -\begin_inset Formula $Z+A\times A\rightarrow(A\rightarrow B)\rightarrow Z+B\times B\quad.$ + to some value of type +\begin_inset Formula $A$ \end_inset +: +\begin_inset Formula +\[ +m=r^{:A\rightarrow T}\rightarrow f^{:A\rightarrow B}\rightarrow b^{:B}\rightarrow r(???^{:A})\quad. +\] -\end_layout +\end_inset -\begin_layout Standard +However, we do not have any values of type +\begin_inset Formula $A$ +\end_inset -\series bold -(b) -\series default - -\begin_inset Formula $A+Z\rightarrow B+Z\rightarrow(A\rightarrow B\rightarrow C)\rightarrow C+Z\quad.$ +. + We have a function +\begin_inset Formula $f^{:A\rightarrow B}$ \end_inset + that +\emph on +consumes +\emph default + values of type +\begin_inset Formula $A$ +\end_inset -\end_layout +, and we cannot use +\begin_inset Formula $f$ +\end_inset -\begin_layout Standard + to produce any values of type +\begin_inset Formula $A$ +\end_inset -\series bold -(c) -\series default - -\begin_inset Formula $P+A\times A\rightarrow(A\rightarrow B)\rightarrow(P\rightarrow A+Q)\rightarrow Q+B\times B\quad.$ +. + So, it seems that we are unable to fill the typed hole +\begin_inset Formula $\text{???}^{:A}$ \end_inset + and implement the function +\begin_inset listings +inline true +status open -\end_layout +\begin_layout Plain Layout -\begin_layout Standard +m +\end_layout -\series bold -(d) -\series default - -\begin_inset Formula $\text{flatMap}^{E,A,B}:\text{Read}^{E,A}\rightarrow(A\rightarrow\text{Read}^{E,B})\rightarrow\text{Read}^{E,B}\quad.$ \end_inset - +. \end_layout \begin_layout Standard +In order to verify that +\begin_inset listings +inline true +status open -\series bold -(e) -\series default - -\begin_inset Formula $\text{State}^{S,A}\rightarrow\left(S\times A\rightarrow B\right)\rightarrow\text{State}^{S,B}\quad.$ -\end_inset - +\begin_layout Plain Layout +m \end_layout -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-7" - \end_inset - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Exercise-7" -plural "false" -caps "false" -noprefix "false" + is unimplementable, we need to prove that the logical formula: +\begin_inset Formula +\begin{equation} +\forall(\alpha,\beta,\tau).\,(\alpha\Rightarrow\tau)\Rightarrow(\alpha\Rightarrow\beta)\Rightarrow(\beta\Rightarrow\tau)\label{eq:ch-example-boolean-formula-3} +\end{equation} \end_inset -* +is not true in the constructive logic. + We could use the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +curryhoward \end_layout -\begin_layout Standard -Denote -\begin_inset Formula $\text{Cont}^{R,T}\triangleq\left(T\rightarrow R\right)\rightarrow R$ \end_inset - and implement the functions: + library for that: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +@ def m[A, B, T] : (A => T) => (A => B) => B => T = implement \end_layout -\begin_layout Standard +\begin_layout Plain Layout -\series bold -(a) -\series default - -\begin_inset Formula $\text{map}^{R,T,U}:\text{Cont}^{R,T}\rightarrow(T\rightarrow U)\rightarrow\text{Cont}^{R,U}\quad.$ -\end_inset +cmd1.sc:1: type (A => T) => (A => B) => B => T cannot be implemented +\end_layout +\begin_layout Plain Layout +def m[A, B, T] : (A => T) => (A => B) => B => T = implement \end_layout -\begin_layout Standard +\begin_layout Plain Layout -\series bold -(b) -\series default - -\begin_inset Formula $\text{flatMap}^{R,T,U}:\text{Cont}^{R,T}\rightarrow(T\rightarrow\text{Cont}^{R,U})\rightarrow\text{Cont}^{R,U}\quad.$ -\end_inset + ^ +\end_layout +\begin_layout Plain Layout +Compilation Failed \end_layout -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:ch-Exercise-8" +\end_inset +Another way is to check whether this formula is true in Boolean logic. + A formula that holds in constructive logic will always hold in Boolean + logic, because all rules shown in Section +\begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-Exercise-8" +reference "subsec:The-rules-of-proof" plural "false" caps "false" noprefix "false" \end_inset -* -\end_layout - -\begin_layout Standard -Denote -\begin_inset Formula $\text{Sel}^{Z,T}\triangleq\left(T\rightarrow Z\right)\rightarrow T$ + preserve Boolean truth values (see Section +\begin_inset space ~ \end_inset - and implement the functions: -\end_layout -\begin_layout Standard +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Relationship-between-Boolean" +plural "false" +caps "false" +noprefix "false" -\series bold -(a) -\series default - -\begin_inset Formula $\text{map}^{Z,A,B}:\text{Sel}^{Z,A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Sel}^{Z,B}\quad.$ \end_inset - + for a proof). + It follows that any formula that fails to hold in Boolean logic will also + not hold in constructive logic. + \end_layout \begin_layout Standard +It is relatively easy to check whether a given Boolean formula is always + equal to +\begin_inset Formula $True$ +\end_inset -\series bold -(b) -\series default - -\begin_inset Formula $\text{flatMap}^{Z,A,B}:\text{Sel}^{Z,A}\rightarrow(A\rightarrow\text{Sel}^{Z,B})\rightarrow\text{Sel}^{Z,B}\quad.$ +. + Simplifying Eq. +\begin_inset space ~ \end_inset - -\end_layout - -\begin_layout Section -Discussion and further developments -\end_layout - -\begin_layout Subsection -Using the Curry-Howard correspondence for writing code -\end_layout - -\begin_layout Standard -The CH correspondence is used in two practically important reasoning tasks: - checking whether a type signature can be implemented as a fully parametric - function, and determining whether two types are equivalent. - For the first task, we map type expressions into formulas in the constructive - logic and apply the proof rules of that logic. - For the second task, we map type expressions into -\emph on -arithmetic -\emph default - formulas and apply the ordinary rules of arithmetic. -\end_layout - -\begin_layout Standard -Although tools such as the -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -curryhoward -\end_layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-boolean-formula-3" +plural "false" +caps "false" +noprefix "false" \end_inset - library can sometimes derive code from types, it is beneficial if a programmer - is able to derive an implementation by hand or to determine that an implementat -ion is impossible. - For instance, the programmer should recognize that the type signature: -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -def f[A, B]: A => (A => B) => B -\end_layout +) with the rules of Boolean logic, we find: +\begin_inset Formula +\begin{align*} + & (\alpha\Rightarrow\tau)\,\gunderline{\Rightarrow}\,(\alpha\Rightarrow\beta)\,\gunderline{\Rightarrow}\,(\beta\Rightarrow\tau)\\ +\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & =\neg(\gunderline{\alpha\Rightarrow\tau})\vee\neg(\gunderline{\alpha\Rightarrow\beta})\vee(\gunderline{\beta\Rightarrow\tau})\\ +\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & =\gunderline{\neg(\neg\alpha\vee\tau)}\vee\gunderline{\neg(\neg\alpha\vee\beta)}\vee(\neg\beta\vee\tau)\\ +\text{use de Morgan's law}:\quad & =\left(\alpha\wedge\neg\tau\right)\vee\gunderline{\left(\alpha\wedge\neg\beta\right)\vee\neg\beta}\vee\tau\\ +\text{use identity }(p\wedge q)\vee q=q:\quad & =\gunderline{\left(\alpha\wedge\neg\tau\right)}\vee\neg\beta\vee\gunderline{\tau}\\ +\text{use identity }(p\wedge\neg q)\vee q=p\vee q:\quad & =\alpha\vee\neg\beta\vee\tau\quad. +\end{align*} \end_inset -has only one fully parametric implementation, while the following two type - signatures have none: -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -def g[A, B]: A => (B => A) => B -\end_layout - -\begin_layout Plain Layout - -def h[A, B]: ((A => B) => A) => A -\end_layout - +This formula is not identically +\begin_inset Formula $True$ \end_inset -Exercises in this chapter help to build up the required technique and intuition. - The two main guidelines for code derivation are: -\begin_inset Quotes eld +: it is +\begin_inset Formula $False$ \end_inset -values of parametric types cannot be constructed from scratch -\begin_inset Quotes erd + when +\begin_inset Formula $\alpha=\tau=False$ \end_inset and -\begin_inset Quotes eld -\end_inset - -one must hard-code the decision to return a chosen part of a disjunctive - type when no other disjunctive value is given -\begin_inset Quotes erd +\begin_inset Formula $\beta=True$ \end_inset . - These guidelines can be justified by referring to the rigorous rules of - proof (Table + So, Eq. \begin_inset space ~ \end_inset - +( \begin_inset CommandInset ref LatexCommand ref -reference "tab:Proof-rules-for-constructive-logic" +reference "eq:ch-example-boolean-formula-3" plural "false" caps "false" noprefix "false" \end_inset -). - Sequents producing a value of type -\begin_inset Formula $A$ -\end_inset - - can be proved only if there is a premise containing -\begin_inset Formula $A$ -\end_inset - - or a function that returns a value of type -\begin_inset Formula $A$ -\end_inset - -. -\begin_inset Foot -status collapsed +) is not true in Boolean logic, therefore it is also not true in constructive + logic. + By the CH correspondence, we conclude that the type signature of +\begin_inset listings +inline true +status open \begin_layout Plain Layout -This is proved rigorously in the paper -\family typewriter - -\begin_inset CommandInset href -LatexCommand href -target "https://research-repository.st-andrews.ac.uk/handle/10023/8824" -literal "false" - -\end_inset +m +\end_layout -\family default - (2016) by R. -\begin_inset space ~ \end_inset -Dyckhoff. -\begin_inset Index idx -status collapsed - -\begin_layout Plain Layout -Roy Dyckhoff + cannot be implemented by a fully parametric function. \end_layout -\end_inset +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-solvedExample-7" - See the -\begin_inset Quotes eld \end_inset -Theorem -\begin_inset Quotes erd -\end_inset - in section 6 ( -\begin_inset Quotes eld -\end_inset +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-solvedExample-7" +plural "false" +caps "false" +noprefix "false" -Goal-directed pruning -\begin_inset Quotes erd \end_inset -). -\end_layout -\end_inset +\end_layout - One can derive a disjunction without hard-coding only if one already has - a disjunction in the premises (and then the rule -\begin_inset Quotes eld +\begin_layout Standard +Define the type constructor +\begin_inset Formula $P^{A}\triangleq\bbnum 1+A+A$ \end_inset -use + and implement \begin_inset listings inline true status open \begin_layout Plain Layout -Either +map \end_layout \end_inset + for it: +\begin_inset Formula +\[ +\text{map}^{A,B}:P^{A}\rightarrow(A\rightarrow B)\rightarrow P^{B}\quad. +\] -\begin_inset Quotes erd \end_inset - could apply). -\end_layout - -\begin_layout Standard -Throughout this chapter, we require all code to be fully parametric. - This is because the CH correspondence gives useful, non-trivial results - only for parameterized types and fully parametric code. - For concrete, non-parameterized types ( +To check that \begin_inset listings inline true status open \begin_layout Plain Layout -Int +map \end_layout \end_inset -, + preserves information, verify the law \begin_inset listings inline true status open \begin_layout Plain Layout -String +map(p)(x => x) == p \end_layout \end_inset -, etc.), one can always produce -\emph on -some -\emph default - values even with no previous data. - So, the propositions -\begin_inset Formula $\mathcal{CH}(\text{Int})$ -\end_inset - - or -\begin_inset Formula $\mathcal{CH}(\text{String})$ -\end_inset - - are always true. -\end_layout - -\begin_layout Standard -Consider the function + for all \begin_inset listings inline true status open \begin_layout Plain Layout -(x:Int) => x + 1 +p: P[A] \end_layout \end_inset . - Its type signature, +\end_layout + +\begin_layout Subparagraph +Solution +\end_layout + +\begin_layout Standard +It is implied that \begin_inset listings inline true status open \begin_layout Plain Layout -Int => Int +map \end_layout \end_inset -, may be implemented by many other functions, such as + should be fully parametric and information-preserving. + Begin by defining a Scala type constructor for the notation +\begin_inset Formula $P^{A}\triangleq\bbnum 1+A+A$ +\end_inset + +: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -x => x - 1 +sealed trait P[A] \end_layout -\end_inset - -, -\begin_inset listings -inline true -status open - \begin_layout Plain Layout -x => x * 2 +final case class P1[A]() extends P[A] \end_layout -\end_inset +\begin_layout Plain Layout -, etc. - So, the type signature -\begin_inset listings -inline true -status open +final case class P2[A](x: A) extends P[A] +\end_layout \begin_layout Plain Layout -Int => Int +final case class P3[A](x: A) extends P[A] \end_layout \end_inset - is insufficient to specify the code of the function, and deriving code - from that type is not a meaningful task. - Only a fully parametric type signature, such as -\begin_inset Formula $A\rightarrow\left(A\rightarrow B\right)\rightarrow B$ -\end_inset - -, could give enough information for deriving the function's code. - Additionally, we must require the code of functions to be fully parametric. - Otherwise we will be unable to reason about code derivation from type signature -s. +Now we can write code to implement the required type signature. + Each time we have several choices of an implementation, we will choose + to preserve information as much as possible. \end_layout \begin_layout Standard -Validity of a -\begin_inset Formula ${\cal CH}$ -\end_inset +\begin_inset listings +inline false +status open --proposition -\begin_inset Formula ${\cal CH}(T)$ -\end_inset +\begin_layout Plain Layout - means that we can implement -\emph on -some -\emph default - value of the given type -\begin_inset Formula $T$ -\end_inset +def map[A, B]: P[A] => (A => B) => P[B] = +\end_layout -. - But this does not give any information about the properties of that value, - such as whether it satisfies any laws. - This is why type equivalence (which requires the laws of isomorphisms) - is not determined by an equivalence of logical formulas. +\begin_layout Plain Layout + + p => f => p match { \end_layout -\begin_layout Standard -It is useful for programmers to be able to transform type expressions to - equivalent simpler types before starting to write code. - The type notation introduced in this book is designed to help programmers - to recognize patterns in type expressions and to reason about them more - easily. - We have shown that a type equivalence corresponds to -\emph on -each -\emph default - standard arithmetic identity such as -\begin_inset Formula $\left(a+b\right)+c=a+\left(b+c\right)$ -\end_inset +\begin_layout Plain Layout -, -\begin_inset Formula $\left(a\times b\right)\times c=a\times(b\times c)$ -\end_inset + case P1() => P1() // No other choice. +\end_layout -, -\begin_inset Formula $1\times a=a$ -\end_inset +\begin_layout Plain Layout -, -\begin_inset Formula $\left(a+b\right)\times c=a\times c+b\times c$ -\end_inset + case P2(x) => ??? +\end_layout -, and so on. - Because of this, we are allowed to transform and simplify types as if they - were arithmetic expressions, e.g., to rewrite: -\begin_inset Formula -\[ -\bbnum 1\times\left(A+B\right)\times C+D\cong D+A\times C+B\times C\quad. -\] +\begin_layout Plain Layout + + case P3(x) => ??? +\end_layout + +\begin_layout Plain Layout + + } +\end_layout \end_inset -The type notation makes this reasoning more intuitive (for people familiar - with arithmetic). - +In the case +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +P2(x) \end_layout -\begin_layout Standard -These results apply to all type expressions built up using product types, - disjunctive types (also called -\begin_inset Quotes eld \end_inset -sum -\begin_inset Quotes erd +, we are required to produce a value of type +\begin_inset Formula $P^{B}$ \end_inset - types because they correspond to arithmetic sums), and function types (also - called -\begin_inset Quotes eld + from a value +\begin_inset Formula $x^{:A}$ \end_inset -exponential -\begin_inset Quotes erd + and a function +\begin_inset Formula $f^{:A\rightarrow B}$ \end_inset - types because they correspond to arithmetic exponentials). - Type expressions that contain only products and sum types are called -\series bold -polynomial -\series default +. + Since +\begin_inset Formula $P^{B}$ +\end_inset -\begin_inset Index idx + is a disjunctive type with three parts, we can produce a value of type + +\begin_inset Formula $P^{B}$ +\end_inset + + in three different ways: +\begin_inset listings +inline true status open \begin_layout Plain Layout -polynomial type + +P1() \end_layout \end_inset - -\begin_inset Index idx +, +\begin_inset listings +inline true status open \begin_layout Plain Layout -types!polynomial types + +P2(...) \end_layout \end_inset -. -\begin_inset Foot +, and +\begin_inset listings +inline true status open \begin_layout Plain Layout -These types are often called -\begin_inset Quotes eld + +P3(...) +\end_layout + \end_inset -algebraic data types -\begin_inset Index idx +. + If we return +\begin_inset listings +inline true status open \begin_layout Plain Layout -algebraic data types + +P1() \end_layout \end_inset +, we will lose the information about the value +\begin_inset listings +inline true +status open -\begin_inset Quotes erd -\end_inset +\begin_layout Plain Layout - but this book prefers the more precise term -\begin_inset Quotes eld -\end_inset +x +\end_layout -polynomial types -\begin_inset Quotes erd \end_inset . + If we return +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +P3(...) \end_layout \end_inset - Type expressions that also contain function types may be called -\series bold -exponential-polynomial -\series default - -\begin_inset Index idx +, we will preserve the information about +\begin_inset listings +inline true status open \begin_layout Plain Layout -exponential-polynomial type + +x \end_layout \end_inset - + but lose the information \begin_inset Index idx status open \begin_layout Plain Layout -types!exponential-polynomial types +information loss \end_layout \end_inset -. - This book focuses on exponential-polynomial types because they are sufficient - for almost all design patterns used in functional programming. -\end_layout + that the input value was a +\begin_inset listings +inline true +status open -\begin_layout Standard -There are no types corresponding to subtraction or division, so arithmetic - equations such as: -\begin_inset Formula -\begin{align*} -\left(1-t\right)\times\left(1+t\right) & =1-t\times t\quad,\quad\text{ and }\quad\frac{t+t\times t}{t}=1+t\quad, -\end{align*} +\begin_layout Plain Layout + +P2 +\end_layout \end_inset -do not directly yield any type equivalences. - However, consider this well-known formula: -\begin_inset Formula -\[ -\frac{1}{1-t}=1+t+t^{2}+t^{3}+...+t^{n}+...\quad. -\] + rather than a +\begin_inset listings +inline true +status open -\end_inset +\begin_layout Plain Layout -At first sight, this formula appears to involve subtraction, division, and - an infinite series, and so cannot be directly translated into a type equivalenc -e. - However, the formula can be rewritten as: -\begin_inset Formula -\begin{equation} -\frac{1}{1-t}=L(t)\quad\text{ where }\quad L(t)\triangleq1+t+t^{2}+t^{3}+...+t^{n}\times L(t)\quad.\label{eq:ch-example-type-formula-list} -\end{equation} +P3 +\end_layout \end_inset -The definition of -\begin_inset Formula $L(t)$ -\end_inset +. + By returning +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +P2(...) +\end_layout - is finite and only contains additions and multiplications. - So, Eq. -\begin_inset space ~ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-type-formula-list" -plural "false" -caps "false" -noprefix "false" + in that scope, we preserve the entire input information. + +\end_layout + +\begin_layout Standard +The value under +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +P2(...) +\end_layout \end_inset -) can be translated into a type equivalence: -\begin_inset Formula -\begin{equation} -L^{A}\cong1+A+A\times A+A\times A\times A+...+\underbrace{A\times...\times A}_{n\text{ times}}\times\,L^{A}\quad.\label{eq:ch-example-type-expansion-list} -\end{equation} + must be of type +\begin_inset Formula $B$ +\end_inset +, and the only way of getting a value of type +\begin_inset Formula $B$ \end_inset -This type formula (with -\begin_inset Formula $n=1$ + is to apply +\begin_inset Formula $f$ \end_inset -) is equivalent to a recursive definition of the type constructor + to +\begin_inset Formula $x$ +\end_inset + +. + So, we return \begin_inset listings inline true status open \begin_layout Plain Layout -List +P2(f(x)) \end_layout \end_inset -: -\begin_inset Formula -\[ -\text{List}^{A}\triangleq1+A\times\text{List}^{A}\quad. -\] +. +\end_layout -\end_inset +\begin_layout Standard +Similarly, in the case +\begin_inset listings +inline true +status open -The type equivalence -\begin_inset space ~ -\end_inset +\begin_layout Plain Layout -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-type-expansion-list" -plural "false" -caps "false" -noprefix "false" +P3(x) +\end_layout \end_inset -) suggests that we may view the recursive type +, we should return \begin_inset listings inline true status open \begin_layout Plain Layout -List +P3(f(x)) \end_layout \end_inset - heuristically as an -\begin_inset Quotes eld -\end_inset - -infinite disjunction -\begin_inset Quotes erd -\end_inset +. + The final code of +\begin_inset listings +inline true +status open - describing lists of zero, one, two, etc., elements. -\end_layout +\begin_layout Plain Layout -\begin_layout Subsection -Implications for designing new programming languages +map \end_layout -\begin_layout Standard -Today's functional programming practice assumes, at the minimum, that programmer -s will use the six standard type constructions (Section -\begin_inset space ~ \end_inset + is: +\begin_inset listings +inline false +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Type-notation-and-standard-type-constructions" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout -\end_inset +def map[A, B]: P[A] => (A => B) => P[B] = p => f => p match { +\end_layout -) and the eight standard code constructions (Section -\begin_inset space ~ -\end_inset +\begin_layout Plain Layout + case P1() => P1() // No other choice here. +\end_layout -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:The-rules-of-proof" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout -\end_inset + case P2(x) => P2(f(x)) // Preserve information. +\end_layout -). - These constructions are foundational in the sense that they are used to - express all design patterns of functional programming. - A language that does not directly support some of these constructions cannot - be considered a functional programming language. +\begin_layout Plain Layout + + case P3(x) => P3(f(x)) // Preserve information. \end_layout -\begin_layout Standard -A remarkable result of the CH correspondence is that the type system of - any given programming language (functional or not) is mapped into a -\emph on -certain -\emph default - -\emph on -logic -\emph default -, i.e., a system of logical operations and proof rules. - A logical operation will correspond to each of the -\emph on -type -\emph default - constructions available in the programming language. - A proof rule will correspond to each of the available -\emph on -code -\emph default - constructions. - Programming languages that support all the standard type and code constructions - — for instance, OCaml, Haskell, F#, Scala, Swift, Rust, — are mapped into - the constructive logic with all standard logical operations available ( -\begin_inset Formula $True$ -\end_inset +\begin_layout Plain Layout + +} +\end_layout -, -\begin_inset Formula $False$ \end_inset -, disjunction, conjunction, and implication). + \end_layout \begin_layout Standard -Languages such as C, C++, Java, C#, Go are mapped into logics that do not - have the disjunction operation or the constants -\begin_inset Formula $True$ -\end_inset +To verify the given law, we first write a matrix notation for +\begin_inset listings +inline true +status open - and -\begin_inset Formula $False$ -\end_inset +\begin_layout Plain Layout -. - In other words, these languages are mapped into -\emph on -incomplete -\emph default - logics where some true formulas cannot be proved. - Incompleteness of the logic of types will make a programming language unable - to express certain computations, e.g., directly handle data that belongs - to a disjoint domain. - +map \end_layout -\begin_layout Standard -Languages that do not enforce type checking (e.g., Python or JavaScript) are - mapped to inconsistent logics where any proposition can be proved — even - propositions normally considered -\begin_inset Formula $False$ \end_inset -. - The CH correspondence will map such absurd proofs to code that -\emph on -appears -\emph default - to compute a certain value (since the -\begin_inset Formula $\mathcal{CH}$ -\end_inset +: +\begin_inset Formula +\[ +\text{map}^{A,B}\triangleq p^{:\bbnum 1+A+A}\rightarrow f^{:A\rightarrow B}\rightarrow p\triangleright\,\begin{array}{|c||ccc|} + & \bbnum 1 & B & B\\ +\hline \bbnum 1 & \text{id} & \bbnum 0 & \bbnum 0\\ +A & \bbnum 0 & f & \bbnum 0\\ +A & \bbnum 0 & \bbnum 0 & f +\end{array}\quad. +\] --proposition was proved to be -\begin_inset Formula $True$ \end_inset -) although that value is not actually available. - In practice, such code will crash because of a value that has a wrong type, - is -\begin_inset Quotes eld +The required law is written as an equation +\begin_inset Formula $\text{map}\left(p\right)(\text{id})=p$ \end_inset -null -\begin_inset Quotes erd +, called the +\begin_inset Index idx +status open + +\begin_layout Plain Layout +identity laws!of +\family typewriter +map +\end_layout + \end_inset -, or is a pointer to an invalid memory location. - Those errors cannot happen in a programming language whose logic of types - is consistent and whose compiler checks all types at compile time. -\end_layout +\series bold +identity law +\series default +. + Substituting the code notation for +\begin_inset listings +inline true +status open -\begin_layout Standard -So, the CH correspondence gives a mathematically justified procedure for - designing new programming languages. - The procedure has the following steps: -\end_layout +\begin_layout Plain Layout -\begin_layout Itemize -Choose a formal logic that is complete and free of inconsistencies. +map \end_layout -\begin_layout Itemize -For each logical operation, provide a type construction in the language. -\end_layout +\end_inset -\begin_layout Itemize -For each axiom and proof rule of the logic, provide a code construction - in the language. -\end_layout - -\begin_layout Standard -Mathematicians have studied different logics, such as modal logic, temporal - logic, or linear logic. - Compared with the constructive logic, those other logics have some additional - type operations. - For instance, modal logic adds the operations -\begin_inset Quotes eld -\end_inset - -necessarily -\begin_inset Quotes erd -\end_inset - - and -\begin_inset Quotes eld -\end_inset +, we verify the law: +\begin_inset Formula +\begin{align*} +\text{expect to equal }p:\quad & \text{map}\left(p\right)(\text{id})\\ +\text{apply \texttt{map()()} to arguments}:\quad & =p\triangleright\,\begin{array}{||ccc|} +\text{id} & \bbnum 0 & \bbnum 0\\ +\bbnum 0 & \text{id} & \bbnum 0\\ +\bbnum 0 & \bbnum 0 & \text{id} +\end{array}\\ +\text{identity function in matrix notation}:\quad & =p\triangleright\text{id}\\ +\triangleright\text{-notation}:\quad & =\text{id}\left(p\right)=p\quad. +\end{align*} -possibly -\begin_inset Quotes erd \end_inset -, and temporal logic adds the operation -\begin_inset Quotes eld -\end_inset -until -\begin_inset Quotes erd -\end_inset +\end_layout -. - For each logic, mathematicians have determined the minimal complete sets - of operations, axioms, and proof rules that do not lead to inconsistency. - Programming language designers can use this mathematical knowledge by choosing - a logic and translating it into a minimal -\begin_inset Quotes eld -\end_inset +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-solvedExample-8" -core -\begin_inset Quotes erd \end_inset - of a programming language. - Code in that language will be guaranteed -\emph on -never to crash -\emph default - as long as all types match. - This mathematical guarantee (known as -\begin_inset Index idx -status open -\begin_layout Plain Layout -type safety -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-solvedExample-8" +plural "false" +caps "false" +noprefix "false" \end_inset -\series bold -type safety -\series default -) is a powerful help for programmers since it automatically prevents a large - number of coding errors. - So, programmers will benefit if they use languages designed using the CH - correspondence. -\end_layout - -\begin_layout Standard -Practically useful programming languages will of course need more features - than the minimal set of mathematically necessary features derived from - a chosen logic. - Language designers need to make sure that all added features are consistent - with the core language. - -\end_layout - -\begin_layout Standard -At present, it is still not fully understood how a practical programming - language could use, say, modal or linear logic as its logic of types. - Experience suggests that, at least, the operations of the plain constructive - logic should be available. - So, it appears that the six type constructions and the eight code constructions - will remain available in all future languages of functional programming. - -\end_layout - -\begin_layout Standard -It is possible to apply the FP paradigm while writing code in any programming - language. - However, some languages lack certain features that make FP techniques easier - to use in practice. - For example, in a language such as JavaScript, Python, or Ruby, one can - productively use the map/reduce operations but not disjunctive types. - More advanced FP constructions (such as typeclasses) are impractical in - these languages because the required code becomes too hard to read and - to write without errors, which negates the advantages of rigorous reasoning - about functional programs. -\end_layout - -\begin_layout Standard -Some programming languages, such as Haskell and OCaml, were designed specificall -y for advanced use in the FP paradigm. - Other languages, such as F#, Scala, Swift, PureScript, Elm, and Rust, have - different design goals but still support enough FP features to be considered - FP languages. - This book uses Scala, but the same constructions may be implemented in - other FP languages in a similar way. - At the level of detail needed in this book, the differences between languages - such as OCaml, Haskell, F#, Scala, Swift, PureScript, Elm, and Rust, do - not play a significant role. -\end_layout - -\begin_layout Subsection -Practical uses of the void type (Scala's -\family typewriter -Nothing -\family default -) \end_layout \begin_layout Standard -The -\begin_inset Index idx +Implement +\begin_inset listings +inline true status open \begin_layout Plain Layout -void type -\end_layout - -\end_inset - -void type -\begin_inset Foot -status open -\begin_layout Plain Layout -The -\begin_inset Quotes eld -\end_inset +map +\end_layout -void -\begin_inset Quotes erd \end_inset - type as defined in this book is a type with no values. - It is -\emph on -not -\emph default - the same as the + and \begin_inset listings inline true status open \begin_layout Plain Layout -void +flatMap \end_layout \end_inset - keyword in Java or C that denotes functions returning -\begin_inset Quotes eld -\end_inset - -no value -\begin_inset Quotes erd -\end_inset - -. - Those functions are equivalent to Scala functions returning the + for \begin_inset listings inline true status open \begin_layout Plain Layout -Unit -\end_layout - -\end_inset - - type. +Either[L, R] \end_layout \end_inset - (Scala's +, applied to the type parameter \begin_inset listings inline true status open \begin_layout Plain Layout -Nothing +L \end_layout \end_inset -) corresponds to the logical constant -\begin_inset Formula $False$ -\end_inset - . - (The proposition -\begin_inset Quotes eld -\end_inset - +\end_layout -\emph on -the code can compute a value of the void type -\emph default +\begin_layout Subparagraph +Solution +\end_layout -\begin_inset Quotes erd +\begin_layout Standard +For a type constructor, say, +\begin_inset Formula $P^{A}$ \end_inset - is always false.) The void type is used in some theoretical proofs but has - few practical uses. - One use case is for a branch of a +, the standard type signatures for \begin_inset listings inline true status open \begin_layout Plain Layout -match +map \end_layout \end_inset -/ + and \begin_inset listings inline true status open \begin_layout Plain Layout -case +flatMap \end_layout \end_inset - expression that throws an -\begin_inset Index idx -status open + are: +\begin_inset Formula +\[ +\text{map}:P^{A}\rightarrow(A\rightarrow B)\rightarrow P^{B}\quad,\quad\quad\text{flatMap}:P^{A}\rightarrow(A\rightarrow P^{B})\rightarrow P^{B}\quad. +\] -\begin_layout Plain Layout -exception -\end_layout +\end_inset +If a type constructor has more than one type parameter, e.g., +\begin_inset Formula $P^{A,S,T}$ \end_inset -exception instead of returning a value. - In this sense, returning a value of the void type corresponds to a crash - in the program. - So, a +, one can define the functions \begin_inset listings inline true status open \begin_layout Plain Layout -throw +map \end_layout \end_inset - expression is defined as if it returns a value of type + and \begin_inset listings inline true status open \begin_layout Plain Layout -Nothing +flatMap \end_layout \end_inset -. - We can then pretend to convert that -\begin_inset Quotes eld + applied to a chosen type parameter. + For example, when applied to the type parameter +\begin_inset Formula $A$ \end_inset -value -\begin_inset Quotes erd +, the type signatures are: +\begin_inset Formula +\begin{align*} +\text{map} & :P^{A,S,T}\rightarrow(A\rightarrow B)\rightarrow P^{B,S,T}\quad,\\ +\text{flatMap} & :P^{A,S,T}\rightarrow(A\rightarrow P^{B,S,T})\rightarrow P^{B,S,T}\quad. +\end{align*} + \end_inset - (which will never be actually returned) into a value of any other type. - Example -\begin_inset space ~ +Being +\begin_inset Quotes eld \end_inset +applied to the type parameter +\begin_inset Formula $A$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:ch-Example-type-identity-0-to-A" -plural "false" -caps "false" -noprefix "false" +\begin_inset Quotes erd \end_inset - shows how to write a function + means that the other type parameters +\begin_inset Formula $S,T$ +\end_inset + + in +\begin_inset Formula $P^{A,S,T}$ +\end_inset + + remain fixed while the type parameter +\begin_inset Formula $A$ +\end_inset + + is replaced by +\begin_inset Formula $B$ +\end_inset + + in the type signatures of \begin_inset listings inline true status open \begin_layout Plain Layout -absurd[A] +map \end_layout \end_inset - of type + and \begin_inset listings inline true status open \begin_layout Plain Layout -Nothing => A +flatMap \end_layout \end_inset @@ -26938,5071 +26677,5240 @@ Nothing => A \end_layout \begin_layout Standard -To see how this trick is used, consider this code defining a value +For the type \begin_inset listings inline true status open \begin_layout Plain Layout -x +Either[L, R] \end_layout \end_inset -: -\begin_inset listings -inline false -status open + (in the type notation, +\begin_inset Formula $L+R$ +\end_inset -\begin_layout Plain Layout +), we keep the type parameter +\begin_inset Formula $R$ +\end_inset -val x: Double = if (t >= 0.0) math.sqrt(t) else throw new Exception( -\begin_inset Quotes eld + fixed while +\begin_inset Formula $L$ \end_inset -error -\begin_inset Quotes erd + is replaced by +\begin_inset Formula $M$ \end_inset -) -\end_layout +. + So we obtain the type signatures: +\begin_inset Formula +\begin{align*} +\text{map} & :L+R\rightarrow(L\rightarrow M)\rightarrow M+R\quad,\\ +\text{flatMap} & :L+R\rightarrow(L\rightarrow M+R)\rightarrow M+R\quad. +\end{align*} \end_inset -The +Implementing these functions is straightforward: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -else +def map[L,M,R]: Either[L, R] => (L => M) => Either[M, R] = e => f => e match + { \end_layout -\end_inset +\begin_layout Plain Layout - branch does not return a value, but -\begin_inset listings -inline true -status open + case Left(x) => Left(f(x)) +\end_layout \begin_layout Plain Layout -x + case Right(y) => Right(y) \end_layout -\end_inset +\begin_layout Plain Layout - is declared to have type -\begin_inset listings -inline true -status open +} +\end_layout \begin_layout Plain Layout -Double \end_layout -\end_inset +\begin_layout Plain Layout -. - For this code to type-check, both branches must return values of the same - type. - So, the compiler needs to pretend that the -\begin_inset listings -inline true -status open +def flatMap[L,M,R]: Either[L, R] => (L => Either[M, R]) => Either[M, R] + = e => f => e match { +\end_layout \begin_layout Plain Layout -else + case Left(x) => f(x) \end_layout -\end_inset +\begin_layout Plain Layout - branch also returns a value of type -\begin_inset listings -inline true -status open + case Right(y) => Right(y) +\end_layout \begin_layout Plain Layout -Double +} \end_layout \end_inset -. - The compiler first assigns the type +The code notation for these functions is: +\begin_inset Formula +\begin{align*} +\text{map} & \triangleq e^{:L+R}\rightarrow f^{:L\rightarrow M}\rightarrow e\triangleright\,\begin{array}{|c||cc|} + & M & R\\ +\hline L & f & \bbnum 0\\ +R & \bbnum 0 & \text{id} +\end{array}\quad,\\ +\text{flatMap} & \triangleq e^{:L+R}\rightarrow f^{:L\rightarrow M+R}\rightarrow e\triangleright\,\begin{array}{|c||c|} + & M+R\\ +\hline L & f\\ +R & y^{:R}\rightarrow\bbnum 0^{:M}+y +\end{array}\quad. +\end{align*} + +\end_inset + +Note that the code matrix for \begin_inset listings inline true status open \begin_layout Plain Layout -Nothing +flatMap \end_layout \end_inset - to the expression -\begin_inset listings -inline true -status open + cannot be split into the +\begin_inset Formula $M$ +\end_inset -\begin_layout Plain Layout + and +\begin_inset Formula $R$ +\end_inset -throw ... + columns because we do not know in advance which part of the disjunctive + type +\begin_inset Formula $M+R$ +\end_inset + + will be returned when we evaluate +\begin_inset Formula $f(x^{:L})$ +\end_inset + +. \end_layout +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-solvedExample-9" + \end_inset - and then automatically uses the conversion -\begin_inset listings -inline true -status open -\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-solvedExample-9" +plural "false" +caps "false" +noprefix "false" -Nothing => Double +\end_inset + +* \end_layout +\begin_layout Standard +Define a type constructor +\begin_inset Formula $\text{State}^{S,A}\equiv S\rightarrow A\times S$ \end_inset - to convert that type to -\begin_inset listings -inline true -status open + and implement the functions: +\end_layout + +\begin_layout Standard + +\series bold +(a) +\series default + +\begin_inset Formula $\text{pure}^{S,A}:A\rightarrow\text{State}^{S,A}\quad.$ +\end_inset -\begin_layout Plain Layout -Double \end_layout -\end_inset +\begin_layout Standard -. - In this way, types will match in the definition of the value -\begin_inset listings -inline true -status open +\series bold +(b) +\series default + +\begin_inset Formula $\text{map}^{S,A,B}:\text{State}^{S,A}\rightarrow(A\rightarrow B)\rightarrow\text{State}^{S,B}\quad.$ +\end_inset -\begin_layout Plain Layout -x \end_layout -\end_inset +\begin_layout Standard -. +\series bold +(c) +\series default +\begin_inset Formula $\text{flatMap}^{S,A,B}:\text{State}^{S,A}\rightarrow(A\rightarrow\text{State}^{S,B})\rightarrow\text{State}^{S,B}\quad.$ +\end_inset + + \end_layout -\begin_layout Standard -This book does not discuss exceptions in much detail. - The functional programming paradigm does not use exceptions because their - presence significantly complicates reasoning about code. +\begin_layout Subparagraph +Solution \end_layout \begin_layout Standard -As another example of using the void type, suppose an external library implement -s a function: +It is assumed that all functions must be fully parametric and preserve as + much information as possible. + We define the type alias: \begin_inset listings inline false status open \begin_layout Plain Layout -def parallel_run[E, A, B](f: A => Either[E, B]): Either[E, B] = ??? +type State[S, A] = S => (A, S) \end_layout \end_inset -We may imagine that -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -parallel_run(f) \end_layout -\end_inset - - performs some parallel computations using a given function -\begin_inset Formula $f$ -\end_inset +\begin_layout Standard -. - In general, functions -\begin_inset Formula $f^{:A\rightarrow E+B}$ -\end_inset - - may return an error of type -\begin_inset Formula $E$ -\end_inset - - or a result of type -\begin_inset Formula $B$ -\end_inset - -. - Suppose we know that a particular function -\begin_inset Formula $f$ -\end_inset - - never fails to compute its result. - To express that knowledge in code, we may explicitly set the type parameter - -\begin_inset Formula $E$ +\series bold +(a) +\series default + The type signature is +\begin_inset Formula $A\rightarrow S\rightarrow A\times S$ \end_inset - to the void type +, and there is only one implementation: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -Nothing +def pure[S, A]: A => State[S, A] = a => s => (a, s) \end_layout \end_inset - when applying -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -parallel_run -\end_layout +In the code notation, this is written as: +\begin_inset Formula +\[ +\text{pu}^{S,A}\triangleq a^{:A}\rightarrow s^{:S}\rightarrow a\times s\quad. +\] \end_inset -: -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout -parallel_run[Nothing, A, B](f) // Types match only when values f(a) always - are of the form Right(b). - \end_layout +\begin_layout Standard + +\series bold +(b) +\series default + The type signature is: +\begin_inset Formula +\[ +\text{map}^{S,A,B}:(S\rightarrow A\times S)\rightarrow(A\rightarrow B)\rightarrow S\rightarrow B\times S\quad. +\] + \end_inset -Returning an error is now impossible (the type +Begin writing a Scala implementation: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -Nothing +def map[S, A, B]: State[S, A] => (A => B) => State[S, B] = { t => f => s + => ??? } \end_layout \end_inset - has no values). - If the function -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -parallel_run -\end_layout +We need to compute a value of +\begin_inset Formula $B\times S$ +\end_inset + from the curried arguments +\begin_inset Formula $t^{:S\rightarrow A\times S}$ \end_inset - is fully parametric, it will work in the same way with all types -\begin_inset Formula $E$ +, +\begin_inset Formula $f^{:A\rightarrow B}$ \end_inset -, including -\begin_inset Formula $E=\bbnum 0$ +, and +\begin_inset Formula $s^{:S}$ \end_inset . - The code implements our intention via type parameters, giving a compile-time - guarantee of correct results. -\begin_inset Note Note + We begin writing the code of +\begin_inset listings +inline true status open \begin_layout Plain Layout -So far, none of our examples involved the logical -\series bold -negation -\series default - -\begin_inset Index idx -status open -\begin_layout Plain Layout -negation (in logic) +map \end_layout \end_inset - operation. - It is defined as: + using a typed hole: \begin_inset Formula \[ -\neg\alpha\triangleq(\alpha\Rightarrow False)\quad. +\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow\text{???}^{:B}\times\text{???}^{:S}\quad. \] \end_inset -Its practical use is as limited as that of -\begin_inset Formula $False$ +The only way of getting a value of type +\begin_inset Formula $B$ \end_inset - and the void type. - However, logical negation plays an important role in Boolean logic, which - we will discuss next. -\end_layout - + is by applying +\begin_inset Formula $f$ \end_inset - -\end_layout - -\begin_layout Subsection -Relationship between Boolean logic and constructive logic -\begin_inset CommandInset label -LatexCommand label -name "subsec:Relationship-between-Boolean" - + to a value of type +\begin_inset Formula $A$ \end_inset - -\end_layout +: +\begin_inset Formula +\[ +\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow f(\text{???}^{:A})\times\text{???}^{:S}\quad. +\] -\begin_layout Standard -We have seen that some true theorems of Boolean logic are not true in constructi -ve logic. - For example, the Boolean identities -\begin_inset Formula $\neg\left(\neg\alpha\right)=\alpha$ \end_inset - and -\begin_inset Formula $\left(\alpha\Rightarrow\beta\right)=(\neg\alpha\vee\beta)$ +The only possibility of filling the typed hole +\begin_inset Formula $\text{???}^{:A}$ \end_inset - do not hold in the constructive logic. - However, as we will now show, any theorem of constructive logic is also - a theorem of Boolean logic. - The reason is that all eight rules of constructive logic (Section -\begin_inset space ~ + is to apply +\begin_inset Formula $t$ \end_inset - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:The-rules-of-proof" -plural "false" -caps "false" -noprefix "false" - + to a value of type +\begin_inset Formula $S$ \end_inset -) are also true in Boolean logic. -\end_layout - -\begin_layout Standard -To verify that a formula is true in Boolean logic, it is sufficient to - check that the value of the formula is -\begin_inset Formula $True$ +. + We already have such a value, +\begin_inset Formula $s^{:S}$ \end_inset - for all possible truth values ( -\begin_inset Formula $True$ +. + Computing +\begin_inset Formula $t(s)$ \end_inset - or -\begin_inset Formula $False$ + yields a pair of type +\begin_inset Formula $A\times S$ \end_inset -) of its variables. - A sequent such as -\begin_inset Formula $\alpha,\beta\vdash\gamma$ +, from which we may take the first part (of type +\begin_inset Formula $A$ \end_inset - is true in Boolean logic if and only if -\begin_inset Formula $\gamma=True$ +) to fill the typed hole +\begin_inset Formula $\text{???}^{:A}$ \end_inset - under the assumption that -\begin_inset Formula $\alpha=\beta=True$ +. + The second part of the pair is a value of type +\begin_inset Formula $S$ \end_inset -. - So, the sequent -\begin_inset Formula $\alpha,\beta\vdash\gamma$ + that we may use to fill the second typed hole, +\begin_inset Formula $\text{???}^{:S}$ \end_inset - is translated into the Boolean formula: -\begin_inset Formula -\[ -\alpha,\beta\vdash\gamma=\left(\left(\alpha\wedge\beta\right)\Rightarrow\gamma\right)=\left(\neg\alpha\vee\neg\beta\vee\gamma\right)\quad. -\] +. + So, the Scala code is: +\begin_inset listings +lstparams "numbers=left" +inline false +status open -\end_inset +\begin_layout Plain Layout -Table -\begin_inset space ~ -\end_inset +def map[S, A, B]: State[S, A] => (A => B) => State[S, B] = { +\end_layout +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Proof-rules-of-constructive-and-boolean" -plural "false" -caps "false" -noprefix "false" + t => f => s => +\end_layout -\end_inset +\begin_layout Plain Layout - translates all proof rules of Section -\begin_inset space ~ -\end_inset + val (a, s2) = t(s) +\end_layout +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:The-rules-of-proof" -plural "false" -caps "false" -noprefix "false" + (f(a), s2) // We could also return `(f(a), s)` here. +\end_layout -\end_inset +\begin_layout Plain Layout - into Boolean formulas. - The first two lines are axioms, while the subsequent lines are Boolean - theorems that can be verified by calculation. +} \end_layout -\begin_layout Standard -\begin_inset Float table -wide false -sideways false -status open +\end_inset -\begin_layout Plain Layout -\align center -\begin_inset Tabular - - - - - - -\begin_inset Text +Why not return the original value +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\series bold -\size small -Constructive logic +s \end_layout \end_inset - - -\begin_inset Text + + in the tuple +\begin_inset Formula $B\times S$ +\end_inset + +, instead of the new value +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\series bold -\size small -Boolean logic +s2 \end_layout \end_inset - - - - -\begin_inset Text + +? The reason is that we would like to preserve information as much as possible. + If we return +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\size small -\begin_inset Formula $\frac{}{\Gamma\vdash{\cal CH}(\bbnum 1)}\quad(\text{create unit})$ +(f(a), s) +\end_layout + \end_inset + in line 4, we will have discarded the computed value +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +s2 \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +, which is a loss of information. +\end_layout -\size small -\begin_inset Formula $\neg\Gamma\vee True=True$ -\end_inset +\begin_layout Standard +To write the code notation for +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +map \end_layout \end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $\frac{~}{\Gamma,\alpha\vdash\alpha}\quad(\text{use arg})$ +, we need to destructure the pair that +\begin_inset Formula $t(s)$ \end_inset + returns. + We can write explicit destructuring code like this: +\begin_inset Formula +\[ +\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow(a^{:A}\times s_{2}^{:S}\rightarrow f(a)\times s_{2})(t(s))\quad. +\] -\end_layout +\end_inset +If we temporarily denote by +\begin_inset Formula $q$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout + the following destructuring function: +\begin_inset Formula +\[ +q\triangleq(a^{:A}\times s_{2}^{:S}\rightarrow f(a)\times s_{2})\quad, +\] -\size small -\begin_inset Formula $\neg\Gamma\vee\neg\alpha\vee\alpha=True$ \end_inset +we will notice that the expression +\begin_inset Formula $s\rightarrow q(t(s))$ +\end_inset -\end_layout - + is a function composition applied to +\begin_inset Formula $s$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout +. + So, we rewrite +\begin_inset Formula $s\rightarrow q(t(s))$ +\end_inset -\size small -\begin_inset Formula $\frac{\Gamma,\alpha\vdash\beta}{\Gamma\vdash\alpha\Rightarrow\beta}\quad(\text{create function})$ + as the composition +\begin_inset Formula $t\bef q$ \end_inset + and obtain shorter code: +\begin_inset Formula +\[ +\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow t\bef(a^{:A}\times s^{:S}\rightarrow f(a)\times s)\quad. +\] + +\end_inset +Shorter formulas are often easier to reason about in derivations, although + not necessarily easier to read when converted to program code. \end_layout -\end_inset - - -\begin_inset Text +\begin_layout Standard -\begin_layout Plain Layout +\series bold +(c) +\series default + The required type signature is: +\begin_inset Formula +\[ +\text{flatMap}^{S,A,B}:(S\rightarrow A\times S)\rightarrow(A\rightarrow S\rightarrow B\times S)\rightarrow S\rightarrow B\times S\quad. +\] -\size small -\begin_inset Formula $\left(\neg\Gamma\vee\neg\alpha\vee\beta\right)=\left(\neg\Gamma\vee\left(\alpha\Rightarrow\beta\right)\right)$ \end_inset +We perform code reasoning with typed holes: +\begin_inset Formula +\[ +\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow\text{???}^{:B\times S}\quad. +\] -\end_layout +\end_inset +To fill +\begin_inset Formula $\text{???}^{:B\times S}$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout +, we need to apply +\begin_inset Formula $f$ +\end_inset -\size small -\begin_inset Formula $\frac{\Gamma\vdash\alpha\quad\quad\Gamma\vdash\alpha\Rightarrow\beta}{\Gamma\vdash\beta}\quad(\text{use function})$ + to some arguments, since +\begin_inset Formula $f$ \end_inset + is the only function that returns any values of type +\begin_inset Formula $B$ +\end_inset -\end_layout +. + Applying +\begin_inset Formula $f$ +\end_inset + to two values will yield a value of type +\begin_inset Formula $B\times S$ \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +, just as we need: +\begin_inset Formula +\[ +\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow f(\text{???}^{:A})(\text{???}^{:S})\quad. +\] -\size small -\begin_inset Formula $\left(\left(\neg\Gamma\vee\alpha\right)\wedge\left(\neg\Gamma\vee\left(\alpha\Rightarrow\beta\right)\right)\right)\Rightarrow\left(\neg\Gamma\vee\beta\right)$ \end_inset +To fill the new typed holes, we need to apply +\begin_inset Formula $t$ +\end_inset -\end_layout - + to an argument of type +\begin_inset Formula $S$ \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout +. + We have only one given value +\begin_inset Formula $s^{:S}$ +\end_inset -\size small -\begin_inset Formula $\frac{\Gamma\vdash\alpha\quad\quad\Gamma\vdash\beta}{\Gamma\vdash\alpha\wedge\beta}\quad(\text{create tuple})$ + of type +\begin_inset Formula $S$ \end_inset +, so we must compute +\begin_inset Formula $t(s)$ +\end_inset -\end_layout + and destructure it: +\begin_inset Formula +\[ +\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow\left(a\times s_{2}\rightarrow f(a)(s_{2})\right)(t(s))\quad. +\] \end_inset - - -\begin_inset Text + +Translating this notation into Scala code, we obtain: +\begin_inset listings +inline false +status open \begin_layout Plain Layout -\size small -\begin_inset Formula $\left(\neg\Gamma\vee\alpha\right)\wedge\left(\neg\Gamma\vee\beta\right)=\left(\neg\Gamma\vee\left(\alpha\wedge\beta\right)\right)$ -\end_inset +def flatMap[S, A, B]: State[S, A] => (A => State[S, B]) => State[S, B] = + { +\end_layout +\begin_layout Plain Layout + t => f => s => \end_layout -\end_inset - - - - -\begin_inset Text - \begin_layout Plain Layout -\size small -\begin_inset Formula $\frac{\Gamma\vdash\alpha\wedge\beta}{\Gamma\vdash\alpha}\quad(\text{use tuple-}1)$ -\end_inset - - + val (a, s2) = t(s) \end_layout -\end_inset - - -\begin_inset Text - \begin_layout Plain Layout -\size small -\begin_inset Formula $\left(\neg\Gamma\vee\left(\alpha\wedge\beta\right)\right)\Rightarrow\left(\neg\Gamma\vee\alpha\right)$ -\end_inset + f(a)(s2) // We could also return `f(a)(s)` here, but that + would lose information. +\end_layout +\begin_layout Plain Layout +} \end_layout \end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -\size small -\begin_inset Formula $\frac{\Gamma\vdash\alpha}{\Gamma\vdash\alpha\vee\beta}\quad(\text{create Left})$ -\end_inset +In order to preserve information, we choose not to discard the computed + value +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +s2 \end_layout \end_inset - - -\begin_inset Text -\begin_layout Plain Layout +. +\end_layout -\size small -\begin_inset Formula $\left(\neg\Gamma\vee\alpha\right)\Rightarrow\left(\neg\Gamma\vee\left(\alpha\vee\beta\right)\right)$ -\end_inset +\begin_layout Standard +The code notation for this +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +flatMap \end_layout \end_inset - - - - -\begin_inset Text -\begin_layout Plain Layout + can be simplified to: +\begin_inset Formula +\[ +\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow t\bef\left(a\times s\rightarrow f(a)(s)\right)\quad. +\] -\size small -\begin_inset Formula $\frac{\Gamma\vdash\alpha\vee\beta\quad\quad\Gamma,\alpha\vdash\gamma\quad\quad\Gamma,\beta\vdash\gamma}{\Gamma\vdash\gamma}\quad(\text{use Either})$ \end_inset \end_layout -\end_inset - - -\begin_inset Text +\begin_layout Subsection +Exercises +\begin_inset Index idx +status open \begin_layout Plain Layout - -\size small -\begin_inset Formula $\left(\left(\neg\Gamma\vee\alpha\vee\beta\right)\wedge\left(\neg\Gamma\vee\neg\alpha\vee\gamma\right)\wedge\left(\neg\Gamma\vee\neg\beta\vee\gamma\right)\right)\Rightarrow\left(\neg\Gamma\vee\gamma\right)$ -\end_inset - - +exercises \end_layout -\end_inset - - - - \end_inset \end_layout -\begin_layout Plain Layout -\begin_inset Caption Standard - -\begin_layout Plain Layout -Proof rules of constructive logic are true also in the Boolean logic. +\begin_layout Subsubsection +Exercise \begin_inset CommandInset label LatexCommand label -name "tab:Proof-rules-of-constructive-and-boolean" +name "subsec:ch-Exercise-0" \end_inset -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Exercise-0" +plural "false" +caps "false" +noprefix "false" \end_inset \end_layout -\end_inset +\begin_layout Standard +Find the cardinality of the following Scala type: +\begin_inset listings +inline false +status open +\begin_layout Plain Layout +type P = Option[Boolean => Option[Boolean]] \end_layout -\begin_layout Standard -To simplify the calculations, note that all terms in the formulas contain - the operation -\begin_inset Formula $\left(\neg\Gamma\vee...\right)$ -\end_inset - - corresponding to the context -\begin_inset Formula $\Gamma$ \end_inset -. - Now, if -\begin_inset Formula $\Gamma$ -\end_inset +Show that +\begin_inset listings +inline true +status open - is -\begin_inset Formula $False$ -\end_inset +\begin_layout Plain Layout -, the entire formula becomes automatically -\begin_inset Formula $True$ -\end_inset +P +\end_layout -, and there is nothing else to check. - So, it remains to verify the formula in case -\begin_inset Formula $\Gamma=True$ \end_inset -, and then we can simply omit all instances of -\begin_inset Formula $\neg\Gamma$ -\end_inset + is equivalent to +\begin_inset listings +inline true +status open - in the formulas. - Let us show the Boolean derivations for the rules -\begin_inset Quotes eld -\end_inset +\begin_layout Plain Layout +Option[Boolean] => Boolean +\end_layout -\begin_inset Formula $\text{use function}$ \end_inset +, but the equivalence is accidental +\begin_inset Index idx +status open -\begin_inset Quotes erd -\end_inset +\begin_layout Plain Layout +type equivalence!accidental +\end_layout - and -\begin_inset Quotes eld \end_inset - -\begin_inset Formula $\text{use Either}$ + and not +\begin_inset Quotes eld \end_inset - +natural \begin_inset Quotes erd \end_inset -; other formulas are checked in a similar way: -\begin_inset Formula -\begin{align*} -\text{formula ``use function''}:\quad & \left(\alpha\wedge\left(\alpha\Rightarrow\beta\right)\right)\Rightarrow\beta\\ -\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & =\gunderline{\neg}(\alpha\,\gunderline{\wedge}\,(\neg\alpha\,\gunderline{\vee}\,\beta))\vee\beta\\ -\text{de Morgan's laws}:\quad & =\gunderline{\neg\alpha\vee(\alpha\wedge\neg\beta)}\vee\beta -\end{align*} +. +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-1-a" \end_inset -\begin_inset Formula -\begin{align*} -\text{identity }p\vee(\neg p\wedge q)=p\vee q\text{ with }p=\neg\alpha\text{ and }q=\beta:\quad & =\neg\alpha\vee\gunderline{\neg\beta\vee\beta}\\ -\text{axiom ``use arg''}:\quad & =True\quad. -\end{align*} +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Exercise-1-a" +plural "false" +caps "false" +noprefix "false" \end_inset -\begin_inset Formula -\begin{align*} -\text{formula ``use Either''}:\quad & \left(\left(\alpha\vee\beta\right)\wedge\left(\alpha\Rightarrow\gamma\right)\wedge\left(\beta\Rightarrow\gamma\right)\right)\Rightarrow\gamma\\ -\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & =\neg\left(\left(\alpha\vee\beta\right)\wedge\left(\neg\alpha\vee\gamma\right)\wedge\left(\neg\beta\vee\gamma\right)\right)\vee\gamma\\ -\text{de Morgan's laws}:\quad & =\left(\neg\alpha\wedge\neg\beta\right)\vee\gunderline{\left(\alpha\wedge\neg\gamma\right)}\vee\gunderline{\left(\beta\wedge\neg\gamma\right)}\vee\gamma\\ -\text{identity }p\vee(\neg p\wedge q)=p\vee q:\quad & =\gunderline{\left(\neg\alpha\wedge\neg\beta\right)\vee\alpha}\vee\beta\vee\gamma\\ -\text{identity }p\vee(\neg p\wedge q)=p\vee q:\quad & =\gunderline{\neg\alpha\vee\alpha}\vee\beta\vee\gamma\\ -\text{axiom ``use arg''}:\quad & =True\quad. -\end{align*} +\end_layout +\begin_layout Standard +Verify the type equivalences +\begin_inset Formula $A+A\cong\bbnum 2\times A$ \end_inset -Since each proof rule of the constructive logic is translated into a true - formula in Boolean logic, it follows that a proof tree in the constructive - logic will be translated into a tree of Boolean formulas that have value - -\begin_inset Formula $True$ + and +\begin_inset Formula $A\times A\cong\bbnum 2\rightarrow A$ \end_inset - for each axiom or proof rule. - The result is that any constructive proof for a sequent such as -\begin_inset Formula $\emptyset\vdash f(\alpha,\beta,\gamma)$ +, where +\begin_inset Formula $\bbnum 2$ \end_inset - is translated into a chain of Boolean implications that look like this: -\begin_inset Formula -\[ -True=(...)\Rightarrow(...)\Rightarrow...\Rightarrow f(\alpha,\beta,\gamma)\quad. -\] + denotes the +\begin_inset listings +inline true +status open -\end_inset +\begin_layout Plain Layout -Since -\begin_inset Formula $\left(True\Rightarrow\alpha\right)=\alpha$ -\end_inset +Boolean +\end_layout -, this chain proves the Boolean formula -\begin_inset Formula $f(\alpha,\beta,\gamma)$ \end_inset -. + type. \end_layout -\begin_layout Standard -For example, the proof tree shown in Figure -\begin_inset space ~ +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-1" + \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "fig:Proof-of-the-sequent-example-2" +reference "subsec:ch-Exercise-1" plural "false" caps "false" noprefix "false" -\end_inset - - is translated into: -\begin_inset Formula -\begin{align*} -\text{axiom ``use arg''}:\quad & True=\left(\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\neg\alpha\vee\alpha\right)\\ -\text{rule ``create function''}:\quad & \quad\Rightarrow\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\alpha\right)\quad.\\ -\text{axiom ``use arg''}:\quad & True=\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\quad.\\ -\text{rule ``use function''}:\quad & True\Rightarrow\left(\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\beta\right)\\ -\text{rule ``create function''}:\quad & \quad\Rightarrow\left(\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\Rightarrow\beta\right)\quad. -\end{align*} - \end_inset \end_layout \begin_layout Standard -It is easier to check Boolean truth tables than to find a proof tree in - constructive logic (or to establish that no proof tree exists). - If we find that a formula is -\emph on -not -\emph default - true in Boolean logic, we know it is also not true in constructive logic. - This gives us a quick way of proving that some type signatures are -\emph on -not -\emph default - implementable as fully parametric functions. - However, if a formula is true in Boolean logic, it does not follow that - the formula is also true in the constructive logic. +Show that +\begin_inset Formula $A\Rightarrow(B\vee C)\neq(A\Rightarrow B)\wedge(A\Rightarrow C)$ +\end_inset + + in constructive and Boolean logic. \end_layout -\begin_layout Standard -In addition to formulas shown in Table -\begin_inset space ~ +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-solvedExample-5-1" + \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "tab:Logical-formulas-not-Boolean-theorems" +reference "subsec:ch-solvedExample-5-1" plural "false" caps "false" noprefix "false" \end_inset - (Section -\begin_inset space ~ + +\end_layout + +\begin_layout Standard +Verify the type equivalence +\begin_inset Formula $\left(A\rightarrow B\times C\right)\cong\left(A\rightarrow B\right)\times\left(A\rightarrow C\right)$ +\end_inset + + with full proofs. +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-type-identity-4" + \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-Motivation-and-first-examples" +reference "subsec:ch-Exercise-type-identity-4" plural "false" caps "false" noprefix "false" \end_inset -), here are three more examples of formulas that are -\emph on -not -\emph default - true in Boolean logic: -\begin_inset Formula -\[ -\forall\alpha.\,\alpha\quad,\quad\quad\forall(\alpha,\beta).\,\alpha\Rightarrow\beta\quad,\quad\quad\forall(\alpha,\beta).\,(\alpha\Rightarrow\beta)\Rightarrow\beta\quad. -\] - -\end_inset -These formulas are also -\emph on -not -\emph default - true in the constructive logic. \end_layout -\begin_layout Subsection -The constructive logic and the law of excluded middle +\begin_layout Standard +Use known rules to verify the type equivalences without need for proofs: \end_layout \begin_layout Standard -Computations in the Boolean logic are often performed using truth tables. - It is perhaps surprising that the proof rules of the constructive logic - are -\emph on -not -\emph default - equivalent to checking whether some propositions are -\begin_inset Formula $True$ -\end_inset - - or -\begin_inset Formula $False$ -\end_inset - via a truth table. - A general form of this statement was proved by K. -\begin_inset space ~ +\series bold +(a) +\series default + +\begin_inset Formula $\left(A+B\right)\times\left(A\rightarrow B\right)\cong A\times\left(A\rightarrow B\right)+\left(\bbnum 1+A\rightarrow B\right)\quad.$ \end_inset -G -\begin_inset ERT -status open - -\begin_layout Plain Layout +\end_layout -\backslash +\begin_layout Standard -\begin_inset Quotes erd +\series bold +(b) +\series default + +\begin_inset Formula $\left(A\times(\bbnum 1+A)\rightarrow B\right)\cong\left(A\rightarrow B\right)\times\left(A\rightarrow A\rightarrow B\right)\quad.$ \end_inset -o -\end_layout -\end_inset +\end_layout -del -\begin_inset Index idx -status open +\begin_layout Standard -\begin_layout Plain Layout -Kurt@Kurt G -\begin_inset ERT -status open +\series bold +(c) +\series default + +\begin_inset Formula $A\rightarrow\left(\bbnum 1+B\right)\rightarrow C\times D\cong\left(A\rightarrow C\right)\times\left(A\rightarrow D\right)\times\left(A\times B\rightarrow C\right)\times\left(A\times B\rightarrow D\right)\quad.$ +\end_inset -\begin_layout Plain Layout +\end_layout -\backslash +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-2" -\begin_inset Quotes erd \end_inset -o -\end_layout + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Exercise-2" +plural "false" +caps "false" +noprefix "false" \end_inset -del -\end_layout -\end_inset +\end_layout - in 1932. -\begin_inset Foot +\begin_layout Standard +Write the type notation for +\begin_inset listings +inline true status open \begin_layout Plain Layout -See -\family typewriter - -\begin_inset CommandInset href -LatexCommand href -name "plato.stanford.edu/entries/intuitionistic-logic-development/" -target "https://plato.stanford.edu/entries/intuitionistic-logic-development/#SomeEarlResu" -literal "false" - -\end_inset - +Either[(A, Int), Either[(A, Char), (A, Float)]] \end_layout \end_inset - In this sense, constructive logic does -\emph on -not -\emph default - imply that every proposition is either -\begin_inset Formula $True$ -\end_inset - - or -\begin_inset Formula $False$ +. + Transform this type into an equivalent type of the form +\begin_inset Formula $A\times(...)$ \end_inset . - This is not intuitive and requires getting used to. - Reasoning in the constructive logic must use the axioms and derivation - rules directly, instead of truth tables. \end_layout -\begin_layout Standard -The reason Boolean logic can use truth tables is that every Boolean proposition - is either -\begin_inset Formula $True$ -\end_inset - - or -\begin_inset Formula $False$ -\end_inset - -. - This can be written as the formula -\begin_inset Formula $\forall\alpha.\,(\neg\alpha\vee\alpha=True)$ -\end_inset +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-3" -. - Table -\begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "tab:Proof-rules-of-constructive-and-boolean" +reference "subsec:ch-Exercise-3" plural "false" caps "false" noprefix "false" \end_inset - uses the Boolean identity -\begin_inset Formula $\left(\alpha\Rightarrow\beta\right)=(\neg\alpha\vee\beta)$ -\end_inset - -, which does not hold in the constructive logic, to translate the constructive - axiom -\begin_inset Quotes eld -\end_inset +\end_layout -\begin_inset Formula $\text{use arg}$ +\begin_layout Standard +Define a type +\begin_inset Formula $\text{OptE}^{T,A}\triangleq\bbnum 1+T+A$ \end_inset + and implement information-preserving +\begin_inset listings +inline true +status open -\begin_inset Quotes erd -\end_inset +\begin_layout Plain Layout - into the Boolean axiom -\begin_inset Formula $\neg\alpha\vee\alpha=True$ -\end_inset +map +\end_layout -. - The formula -\begin_inset Formula $\forall\alpha.\,\neg\alpha\vee\alpha=True$ \end_inset - is known as the -\series bold -law of excluded middle -\series default -. -\begin_inset Foot + and +\begin_inset listings +inline true status open \begin_layout Plain Layout -\family typewriter -\begin_inset CommandInset href -LatexCommand href -name "https://en.wikipedia.org/wiki/Law_of_excluded_middle" -target "https://en.wikipedia.org/wiki/Law_of_excluded_middle" -literal "false" +flatMap +\end_layout \end_inset + for it, applied to the type parameter +\begin_inset Formula $A$ +\end_inset -\end_layout - +. + Get the same result using the equivalent type +\begin_inset Formula $(\bbnum 1+A)+T$ \end_inset - -\begin_inset Index idx +, i.e., +\begin_inset listings +inline true status open \begin_layout Plain Layout -law of excluded middle -\end_layout - -\end_inset - It is remarkable that the constructive logic -\emph on -does not have -\emph default - the law of excluded middle. - It is neither an axiom nor a derived theorem in constructive logic. +Either[Option[A], T] \end_layout -\begin_layout Standard -To see why, translate the constructive logic formula -\begin_inset Formula $\forall\alpha.\,\neg\alpha\vee\alpha=True$ -\end_inset - - into a type. - The negation operation ( -\begin_inset Formula $\neg\alpha$ -\end_inset - -) is defined as the implication -\begin_inset Formula $\alpha\Rightarrow False$ \end_inset . - So, the formula -\begin_inset Formula $\forall\alpha.\,\neg\alpha\vee\alpha$ -\end_inset + The required type signatures are: +\begin_inset Formula +\begin{align*} +\text{map}^{A,B,T} & :\text{OptE}^{T,A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{OptE}^{T,B}\quad,\\ +\text{flatMap}^{A,B,T} & :\text{OptE}^{T,A}\rightarrow(A\rightarrow\text{OptE}^{T,B})\rightarrow\text{OptE}^{T,B}\quad. +\end{align*} - corresponds to the type -\begin_inset Formula $\forall A.\,\left(A\rightarrow\bbnum 0\right)+A$ \end_inset -. - Can we compute a value of this type via fully parametric code? For that, - we need to compute either a value of type -\begin_inset Formula $A\rightarrow\bbnum 0$ -\end_inset - or a value of type -\begin_inset Formula $A$ -\end_inset +\end_layout -. - This decision needs to be made in advance independently of -\begin_inset Formula $A$ -\end_inset +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-4" -, because the code of a fully parametric function must operate in the same - way for all types. - As we have seen in Example -\begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-Example-type-identity-A-0" +reference "subsec:ch-Exercise-4" plural "false" caps "false" noprefix "false" \end_inset -, a value of type -\begin_inset Formula $A\rightarrow\bbnum 0$ -\end_inset - exists if the type -\begin_inset Formula $A$ -\end_inset +\end_layout - is itself -\begin_inset Formula $\bbnum 0$ -\end_inset +\begin_layout Standard +Implement the +\begin_inset listings +inline true +status open -. - But we do not know in advance whether -\begin_inset Formula $A=\bbnum 0$ -\end_inset +\begin_layout Plain Layout -. - Since there are no values of type -\begin_inset Formula $\bbnum 0$ -\end_inset +map +\end_layout -, and the type parameter -\begin_inset Formula $A$ \end_inset - could be, say, + function for the type constructor \begin_inset listings inline true status open \begin_layout Plain Layout -Int +P[A] \end_layout \end_inset -, we cannot compute a value of type -\begin_inset Formula $A\rightarrow\bbnum 0$ + from Example +\begin_inset space ~ \end_inset -. -\end_layout -\begin_layout Standard -Why is it impossible to implement a value of the type -\begin_inset Formula $\left(A\rightarrow\bbnum 0\right)+A$ -\end_inset +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-solvedExample-2" +plural "false" +caps "false" +noprefix "false" -? Surely, the type -\begin_inset Formula $A$ \end_inset - is either void or not void. - If -\begin_inset Formula $A$ +. + The required type signature is +\begin_inset Formula $P^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow P^{B}$ \end_inset - is void then -\begin_inset Formula $\left(A\rightarrow\bbnum 0\right)\cong\bbnum 1$ -\end_inset +. + Preserve information as much as possible. +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-4-1" - is not void (as Example -\begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:ch-Example-type-identity-A-0" +reference "subsec:ch-Exercise-4-1" plural "false" caps "false" noprefix "false" \end_inset - shows). - So, one of the types in the disjunction -\begin_inset Formula $\left(A\rightarrow\bbnum 0\right)+A$ -\end_inset - should be non-void and have values that we can compute. \end_layout \begin_layout Standard -While this argument is true, it does not help implementing a value of type - -\begin_inset Formula $\left(A\rightarrow\bbnum 0\right)+A$ +For the type constructor +\begin_inset Formula $Q^{T,A}$ \end_inset - via fully parametric code. - It is not enough to know that one of the two values -\begin_inset Quotes eld + defined in Exercise +\begin_inset space ~ \end_inset -should exist -\begin_inset Quotes erd -\end_inset -. - We need to know -\emph on -which -\emph default - of the two values exists, and we need to write code that computes that - value. - That code may not decide what to do depending on whether the type -\begin_inset Formula $A$ -\end_inset +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-type-notation-1" +plural "false" +caps "false" +noprefix "false" - is void, because the code must work in the same way for all types -\begin_inset Formula $A$ \end_inset - (void or not). - As we have seen, that code is impossible to write. -\end_layout +, define the +\begin_inset listings +inline true +status open -\begin_layout Standard -In Boolean logic, one may prove that a value -\begin_inset Quotes eld -\end_inset +\begin_layout Plain Layout -should exist -\begin_inset Quotes erd -\end_inset +map +\end_layout - by showing that the non-existence of a value is contradictory in some way. - However, any practically useful program needs to -\begin_inset Quotes eld \end_inset -construct -\begin_inset Quotes erd -\end_inset + function, preserving information as much as possible: +\begin_inset Formula +\[ +\text{map}^{T,A,B}:Q^{T,A}\rightarrow\left(A\rightarrow B\right)\rightarrow Q^{T,B}\quad. +\] - (i.e., to compute) actual values. - The -\begin_inset Quotes eld \end_inset -constructive -\begin_inset Index idx -status open -\begin_layout Plain Layout -constructive logic \end_layout +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:Exercise-disjunctive-6" + \end_inset -\begin_inset Quotes erd +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-disjunctive-6" +plural "false" +caps "false" +noprefix "false" + \end_inset - logic got its name from this requirement. - So, it is the constructive logic (not the Boolean logic) that provides - correct reasoning about the types of values computable by fully parametric - functional programs. + \end_layout \begin_layout Standard -If we drop the requirement of full parametricity, we -\emph on -could -\emph default - implement the law of excluded middle -\begin_inset Index idx -status open - -\begin_layout Plain Layout -law of excluded middle -\end_layout +Define a recursive type constructor +\begin_inset Formula $\text{Tr}_{3}$ +\end_inset + as +\begin_inset Formula $\text{Tr}_{3}{}^{A}\triangleq\bbnum 1+A\times A\times A\times\text{Tr}_{3}{}^{A}$ \end_inset -. - Special features of Scala (reflection, type tags, and type casts) allow - programmers to compare types as values and to determine what type was given - to a type parameter when a function is applied: + and implement the \begin_inset listings -lstparams "mathescape=true" -inline false +inline true status open \begin_layout Plain Layout -import scala.reflect.runtime.universe._ +map \end_layout -\begin_layout Plain Layout - -def getType[T: TypeTag]: Type = weakTypeOf[T] // Convert the type parameter - T into a special value. -\end_layout +\end_inset -\begin_layout Plain Layout - -def equalTypes[A: TypeTag, B: TypeTag]: Boolean = getType[A] =:= getType[B] - // Compare types A and B. -\end_layout + function for it, with the standard type signature: +\begin_inset Formula $\text{map}^{A,B}:\text{Tr}_{3}{}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Tr}_{3}{}^{B}\quad.$ +\end_inset -\begin_layout Plain Layout \end_layout -\begin_layout Plain Layout - -def excludedMiddle[A: TypeTag]: Either[A, A => Nothing] = // excludedMiddle - has type ${ -\backslash -color{dkgreen} -\backslash -forall A. -\backslash -left(A -\backslash -rightarrow -\backslash -bbnum 0 -\backslash -right)+A}$. -\end_layout +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-5" -\begin_layout Plain Layout +\end_inset - if (equalTypes[A, Nothing]) Right((identity _).asInstanceOf[A => Nothing]) - // Return ${ -\backslash -color{dkgreen} -\backslash -text{id}: -\backslash -bbnum 0 -\backslash -rightarrow -\backslash -bbnum 0}$. -\end_layout -\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Exercise-5" +plural "false" +caps "false" +noprefix "false" - else if (equalTypes[A, Int]) Left(123.asInstanceOf[A]) // Produce - some value of type Int. -\end_layout +\end_inset -\begin_layout Plain Layout - else if (equalTypes[A, Boolean]) Left(true.asInstanceOf[A]) // Produce - some value of type Boolean. \end_layout -\begin_layout Plain Layout - - else ??? // Need to write many more definitions to support - all other Scala types. +\begin_layout Standard +Implement fully parametric, information-preserving functions with the types: \end_layout -\begin_layout Plain Layout +\begin_layout Standard -\end_layout +\series bold +(a) +\series default + +\begin_inset Formula $Z+A\times A\rightarrow(A\rightarrow B)\rightarrow Z+B\times B\quad.$ +\end_inset -\begin_layout Plain Layout -scala> excludedMiddle[Int] \end_layout -\begin_layout Plain Layout +\begin_layout Standard -res0: Either[Int,Int => Nothing] = Left(123) -\end_layout +\series bold +(b) +\series default + +\begin_inset Formula $A+Z\rightarrow B+Z\rightarrow(A\rightarrow B\rightarrow C)\rightarrow C+Z\quad.$ +\end_inset -\begin_layout Plain Layout \end_layout -\begin_layout Plain Layout +\begin_layout Standard -scala> excludedMiddle[Nothing] -\end_layout +\series bold +(c) +\series default + +\begin_inset Formula $P+A\times A\rightarrow(A\rightarrow B)\rightarrow(P\rightarrow A+Q)\rightarrow Q+B\times B\quad.$ +\end_inset -\begin_layout Plain Layout -res1: Either[Nothing,Nothing => Nothing] = Right() \end_layout -\end_inset +\begin_layout Standard -In this code, we check whether -\begin_inset Formula $A=\bbnum 0$ +\series bold +(d) +\series default + +\begin_inset Formula $\text{flatMap}^{E,A,B}:\text{Read}^{E,A}\rightarrow(A\rightarrow\text{Read}^{E,B})\rightarrow\text{Read}^{E,B}\quad.$ \end_inset -. - If so, we can implement -\begin_inset Formula $A\rightarrow\bbnum 0$ -\end_inset - as an identity function of type -\begin_inset Formula $\bbnum 0\rightarrow\bbnum 0$ -\end_inset +\end_layout -. - Otherwise, we know that -\begin_inset Formula $A$ -\end_inset +\begin_layout Standard - is one of the existing Scala types ( -\begin_inset listings -inline true -status open +\series bold +(e) +\series default + +\begin_inset Formula $\text{State}^{S,A}\rightarrow\left(S\times A\rightarrow B\right)\rightarrow\text{State}^{S,B}\quad.$ +\end_inset -\begin_layout Plain Layout -Int \end_layout -\end_inset +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-7" -, -\begin_inset listings -inline true -status open +\end_inset -\begin_layout Plain Layout -Boolean -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Exercise-7" +plural "false" +caps "false" +noprefix "false" \end_inset -, etc.), which are not void and have values that we can simply write down - one by one in the subsequent code. - +* \end_layout \begin_layout Standard -Explicit -\begin_inset Index idx -status open +Denote +\begin_inset Formula $\text{Cont}^{R,T}\triangleq\left(T\rightarrow R\right)\rightarrow R$ +\end_inset -\begin_layout Plain Layout -type casts + and implement the functions: \end_layout -\end_inset +\begin_layout Standard - \series bold -type casts +(a) \series default -, such as -\begin_inset listings -inline true -status open + +\begin_inset Formula $\text{map}^{R,T,U}:\text{Cont}^{R,T}\rightarrow(T\rightarrow U)\rightarrow\text{Cont}^{R,U}\quad.$ +\end_inset -\begin_layout Plain Layout -123.asInstanceOf[A] \end_layout -\end_inset +\begin_layout Standard -, are needed because the Scala compiler cannot know that -\begin_inset listings -inline true -status open +\series bold +(b) +\series default + +\begin_inset Formula $\text{flatMap}^{R,T,U}:\text{Cont}^{R,T}\rightarrow(T\rightarrow\text{Cont}^{R,U})\rightarrow\text{Cont}^{R,U}\quad.$ +\end_inset -\begin_layout Plain Layout -A \end_layout -\end_inset +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:ch-Exercise-8" - is -\begin_inset listings -inline true -status open +\end_inset -\begin_layout Plain Layout -Int -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Exercise-8" +plural "false" +caps "false" +noprefix "false" \end_inset - in the scope where we return -\begin_inset listings -inline true -status open +* +\end_layout -\begin_layout Plain Layout +\begin_layout Standard +Denote +\begin_inset Formula $\text{Sel}^{Z,T}\triangleq\left(T\rightarrow Z\right)\rightarrow T$ +\end_inset -Left(123) + and implement the functions: \end_layout -\end_inset +\begin_layout Standard -. - Without a type cast, the compiler will not accept -\begin_inset listings -inline true -status open +\series bold +(a) +\series default + +\begin_inset Formula $\text{map}^{Z,A,B}:\text{Sel}^{Z,A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Sel}^{Z,B}\quad.$ +\end_inset -\begin_layout Plain Layout -123 \end_layout -\end_inset +\begin_layout Standard - as a value of type -\begin_inset listings -inline true -status open +\series bold +(b) +\series default + +\begin_inset Formula $\text{flatMap}^{Z,A,B}:\text{Sel}^{Z,A}\rightarrow(A\rightarrow\text{Sel}^{Z,B})\rightarrow\text{Sel}^{Z,B}\quad.$ +\end_inset -\begin_layout Plain Layout -A \end_layout -\end_inset - - in that scope. +\begin_layout Section +Discussion and further developments +\end_layout + +\begin_layout Subsection +Using the Curry-Howard correspondence for writing code \end_layout \begin_layout Standard -The method +The CH correspondence is used in two practically important reasoning tasks: + checking whether a type signature can be implemented as a fully parametric + function, and determining whether two types are equivalent. + For the first task, we map type expressions into formulas in the constructive + logic and apply the proof rules of that logic. + For the second task, we map type expressions into +\emph on +arithmetic +\emph default + formulas and apply the ordinary rules of arithmetic. +\end_layout + +\begin_layout Standard +Although tools such as the \begin_inset listings inline true status open \begin_layout Plain Layout -asInstanceOf +curryhoward \end_layout \end_inset - is dangerous because the code + library can sometimes derive code from types, it is beneficial if a programmer + is able to derive an implementation by hand or to determine that an implementat +ion is impossible. + For instance, the programmer should recognize that the type signature: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -x.asInstanceOf[T] +def f[A, B]: A => (A => B) => B \end_layout \end_inset - disables the type checking for the value +has only one fully parametric implementation, while the following two type + signatures have none: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -x +def g[A, B]: A => (B => A) => B +\end_layout + +\begin_layout Plain Layout + +def h[A, B]: ((A => B) => A) => A \end_layout \end_inset +Exercises in this chapter help to build up the required technique and intuition. + The two main guidelines for code derivation are: +\begin_inset Quotes eld +\end_inset + +values of parametric types cannot be constructed from scratch +\begin_inset Quotes erd +\end_inset + + and +\begin_inset Quotes eld +\end_inset + +one must hard-code the decision to return a chosen part of a disjunctive + type when no other disjunctive value is given +\begin_inset Quotes erd +\end_inset + . - This tells the Scala compiler to believe that + These guidelines can be justified by referring to the rigorous rules of + proof (Table +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Proof-rules-for-constructive-logic" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). + Sequents producing a value of type +\begin_inset Formula $A$ +\end_inset + + can be proved only if there is a premise containing +\begin_inset Formula $A$ +\end_inset + + or a function that returns a value of type +\begin_inset Formula $A$ +\end_inset + +. +\begin_inset Foot +status open + +\begin_layout Plain Layout +This is proved rigorously by R. +\begin_inset space ~ +\end_inset + +Dyckhoff +\begin_inset Index idx +status collapsed + +\begin_layout Plain Layout +Roy Dyckhoff +\end_layout + +\end_inset + + as the +\begin_inset Quotes eld +\end_inset + +Theorem +\begin_inset Quotes erd +\end_inset + + in section 6 ( +\begin_inset Quotes eld +\end_inset + +Goal-directed pruning +\begin_inset Quotes erd +\end_inset + +), see +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "https://research-repository.st-andrews.ac.uk/handle/10023/8824" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + One can derive a disjunction without hard-coding only if one already has + a disjunction in the premises (and then the rule +\begin_inset Quotes eld +\end_inset + +use \begin_inset listings inline true status open \begin_layout Plain Layout -x +Either \end_layout \end_inset - has type + +\begin_inset Quotes erd +\end_inset + + could apply). +\end_layout + +\begin_layout Standard +Throughout this chapter, we require all code to be fully parametric. + This is because the CH correspondence gives useful, non-trivial results + only for parameterized types and fully parametric code. + For concrete, non-parameterized types ( \begin_inset listings inline true status open \begin_layout Plain Layout -T +Int \end_layout \end_inset - even when the type +, \begin_inset listings inline true status open \begin_layout Plain Layout -T +String \end_layout \end_inset - is inconsistent with the actually given code of +, etc.), one can always produce +\emph on +some +\emph default + values even with no previous data. + So, the propositions +\begin_inset Formula $\mathcal{CH}(\text{Int})$ +\end_inset + + or +\begin_inset Formula $\mathcal{CH}(\text{String})$ +\end_inset + + are always true. +\end_layout + +\begin_layout Standard +Consider the function \begin_inset listings inline true status open \begin_layout Plain Layout -x +(x:Int) => x + 1 \end_layout \end_inset . - The resulting programs compile but may give unexpected results or crash. - These errors would have been prevented if we did not disable the type checking. - In this book, we will avoid writing such code whenever possible. -\begin_inset Note Comment -status collapsed + Its type signature, +\begin_inset listings +inline true +status open \begin_layout Plain Layout -showing here this is equivalent in Scala just different syntax importantly - non theorems cannot be implemented in code some on theorems are statements - in logic that cannot be derived statements that are false or undereye verbal - examples of these statements are these for all a from one follows a now - this is certainly suspicious in terms of logic what if a were false then - we would have it from true false false that's very obviously wrong and - we cannot implement a function of this type to implement it we would have - to take a unit argument and produce a value of type a where a is arbitrary - type but how can we produce a value of type a of the type that we don't - even know what it is and there is no data for us to produce that value - so it is impossible another example of an impossible type is this type - so from a plus B follows a if you wanted to implement this function you - would have to take a value of disjunction type a plus B and return a value - of type a but how can you do that what exodus Junction type happens to - contain B and no a just B it cannot contain a if it contains a B it's a - disjunction so then we don't have an A and then we again cannot produce - any and having a B which is a completely different arbitrary type doesn't - help us to produce me exactly the same reason shows why we cannot produce - an A a and B given a because that requires a B we cannot produce and also - this is not implementable because we are required to produce an A but all - we have is a function from A to B this function will consume an A if given - only this function cannot possibly produce an A for us but we are required - to produce an A as a result so we cannot and also there is no proof of - this formula in the logic so these examples actually lead us to a natural - question how can we decide given a certain formula whether it is a theorem - in logic and therefore whether it can be implemented in code it is not - obvious consider this example can we write a function with this type in - Scala it is not obvious can we prove this formula it is not clear not quite - obvious right now suppose I were of the opinion that this cannot be proved - but how do I show that this cannot be proved I certainly cannot just try - all possible proofs that would be infinitely many possible proofs that - would give me all kinds of other formulas and that would give me nothing - that I can stand oh how to answer these questions so it is really a very - hard question we are not going to try to answer it on our own we were going - to use the results of mathematicians they have studied these questions - for many many years for centuries logic has been studied since ancient - Greece more than 2,000 years of study all we need to do is to find out - by what name mathematicians call this logic they are probably already studied - it what kind of logic is this that we are using that follows from the type - constructions remember and the very beginning of our consideration we started - with the type constructions that our programming languages have so that's - set of type constructions specifies the set of rules of derivation of the - logic mathematicians call this logic intuitionistic propositional logic - or IPL also they call it constructive propositional logic but it is less - frequently used most frequently used name is this and mathematicians also - call this a non classical logic because this logic is actually different - from the boolean logic that we are familiar with the logic of the values - true and false and their truth tables I assume that you are familiar with - those computations using truth tables and operations and or not in the - boolean logic so actually this logic the logic of types as I call it or - intuitionistic propositional logic is very different from boolean logic - in certain ways it's similar in other ways disjunction for instance works - very differently here's an example consider this sequence if it has given - that from a follows B plus C then either from a follows B or from a follows - C it sounds right from the common-sense point of it if if B plus C Falls - a B or C if I was I'm using plus as a logical or so if B or C follows then - it kind of makes sense either B follows or C Falls indeed this is correct - in the boolean logic which we can find out by writing the truth table so - we enumerate all the possibilities for a B and C to be true or false or - eight such possibilities and for each of those possibilities we write the - truth value of this the truth value of this and we see from the table that - whenever this is true then this is also true in the boolean logic but this - does not hold in the intuitionistic logic for the logic of types well why - does it not hold that's counterintuitive well in fact there is very little - that's intuitive about this so-called intuitionistic logic actually we - need to think differently about this logic we need to think can we implement - an expression of this sequent so implementing it would mean if we're given - this expression we can build an expression of this type so we're given - an expression of type A to B plus C let's say some F of this type can we - build an expression of this type we can this differently by asking can - we implement a function that takes this as an argument and returns this - well we know that this is equivalent one of our derivation rules is that - if you have this sequence then you can also have a sequence that is a function - type from this to this so for the programmer it is easier to reason about - a function taking this as an argument and returning this so how can we - implement this function this function takes F and needs to return a value - of this type so the body of this function if we could implement it and - have to construct a value of type either of something there are only two - ways of constructing a value of type either one is to construct the left - value second is to construct the right value how do we decide whether to - construct the left value or the right value we have to decide it somehow - on the basis of what information can we decide it we don't actually have - any such information what we have here is a function from a to either BC - so given some value of a of type a we could compute f of that value and - then we would have either B or C we could decide them whether to we could - take them that B or that C but that's not what we need to return we don't - need to return either of BC we need to return either of this function or - that function and that function is not yet applied to any a it is it is - too late for us to ask what is the a we already have to return the left - of this or a right of that in other words this type either of something-somethi -ng is not itself a function of a it contains functions away but itself it - cannot be decided on the basis of any assets too late so we need to supply - a left or right so here right away immediately we have to decide whether - this will return a left or a right and we cannot really decide that if - we decide we return the left we must then return a function from A to B - so there's no way for us to construct this function if we're given this - function because this function could sometimes return C instead of B and - then we'll be stuck we cannot do this and we can also return we cannot - also return the right either so it is impossible to implement a function - of this type implication also works a little differently in the intuitionistic - logic here's an example this holds in boolean logic but not in intuitionistic - logic again let's see why how can we compute this given this this function - will give us an e only when given an argument of this type but how can - we produce a value of this type we cannot we don't have information that - will allow us to produce a value of this type a and B are some arbitrary - types remember there is universal quantifier outside of all this for all - a and for all B we're supposed to produce this and that is impossible we - don't have enough data to produce some values type a and so we cannot implement - this function conjunction works kind of the same as in boolean logic so - here's an example this implemented and this is also in boolean logic a - true theorem now in boolean logic the usual way of deciding whether something - is true or something is a theorem is to write a truth table unfortunately - the intuitionistic logic cannot have a truth table it cannot have a fixed - number of truth values even if you allow more than two truth values such - that the validity of formulas the truth of theorems can be decided on the - basis of the truth table this was shown by noodle and this means we should - not actually try to reason about this logic using truth values it is not - very useful even an infinite infinite number of truth values will not help - instead however it turns out that this logic has a decision procedure or - an algorithm and this algorithm is guaranteed either to find the proof - for any given formula of the internation intuitionistic logic or to determine - that there is no proof for that formula the algorithm can also find several - in equivalent proofs if there is a theorem so a theorem could have several - in equivalent proofs and since each proof could be automatically translated - into code of that type it means we could generate several in equivalent - expressions of some type sometimes so that is the situation with this logic - which we discover if we write if we read papers about intuitionistic propositio -nal logic that are available in the literature and their open source projects - on the web such as the gen GHC which is a compiler plugin for haskell this - is another project doing the same thing and for Scala are implemented occurred - the Clary Howard library both of these Scala and Haskell all of these color - and Haskell projects do the same thing they take a type of some expression - for function and generate code for it automatic by translating the type - into sequence finding a proof in this logic using the algorithm and translating - that proof back into code in the way that we have seen in an example it - is interesting that all these provers and there's a few others there's - one more for the idris language I did not mention here they all used the - same decision procedure or the same basic algorithm which is called ljt - which was explained in a paper by dick off here they all side the same - paper and I believe this is so because most other papers on this subject - are unreadable to non-specialists they are written in a very complicated - way or they describe algorithms that are too complicated so I will show - how this works in the rest of this tutorial in order to find out how to - get an algorithm we need to ask well first of all do we have the rules - of derivation that allow us to create an algorithm already here is a summary - of the axioms and the rules of derivation that we have found so far these - are direct translations of the cold expressions that we held in the programming - language in the notation of sequence now there's one other notation for - derivation rules which looks like a fraction like this the numerator is - one or more sequins and the denominator is a sequence and this notation - means in order to derive what is in the denominator you have to present - proofs for what is in the numerator so this is the convention in the literature - this fraction like syntax or notation now we keep in mind that proofs of - sequence are actually just called expressions that have these types as - some variables and this type is the entire expression so these are directly - responding to proofs of this sequence and to the proofs of these derivation - rules and so if we have a proof that operates by combining some of these - axioms and some of these generation rules which directly translate that - back into code now the question is do these rules give us an algorithm - for finding a proof the answer is no how can we use these rules to obtain - an algorithm well suppose we need to prove some sequence like this in order - to prove it we could first see if the sequence is one of the axioms if - so then we have already proved if we know what expression to write now - in this case none of the axioms match this so much means maybe a is a times - B so B here is C and then on the Left we must have C or you must have a - times B now we don't you don't have C on the left as we have because even - that's not the same we also don't have a times B at the premise we have - a but we don't have a times B so these rules don't match the other rules - don't match the premises and the goal either but also these rules so how - can we use them well when the writer must be an implication we don't have - an application on the right here we could try to delete some of the premises - because it's unused well actually it doesn't look like a good idea could - you read a for example and we end up with an really hopeless sequence from - B plus C we cannot get an A ever and so but sounds hopeless so this doesn't - seem to help and changing the order doesn't seem to help much either and - so we cannot find matching rules but actually this sequence is provable - just a clever combination of what axiom to start with and what role to - use and then again some axiom and so on it will give us that time sure - because I know how to write code for this this is not difficult you have - a function with two arguments one of them is a the other is B plus C so - disjunction of either B C and we are supposed to produce a disjunction - of tuple a B or C that's easy look at this disjunction if we have a B in - this disjunction then we can produce a left of the tuple a B because we - always have an A anyway if we have a see in this disjunction then we could - return this part of the disjunction in the right of C and we're done but - unfortunately we see that the rules here do not give us an algorithm for - deciding this we need a better formulation of the logic again mathematicians - need to save us from the situation and they have done so mathematicians - have studied this logic for a long time starting from the early 20th of - the last century the first algorithmic formulation of the logic that was - found is due to Jensen who published what he called the calculus just ignore - the word calculus it means not very much complete and sound calculus means - that he came up with some rules of derivation which are summarized here - such that they are equivalent to these they derive all the same theorems - and only the same theorems so they derive all the stuff that is right and - only that stuff they don't derive any wrong statements it's very hard to - come up with such a system of axioms and derivation rules that are equivalent - to another one in this sense also it's very hard to prove that these are - actually the rules that will give you all the theorems that could be right - in this logic that you can actually derive all the theorems that are right - yet work is already done by mathematicians so we're not going to try to - do it ourselves we're just going to understand how these rules work now - the syntax here is slightly enhanced compared with this the enhancement - is that their names pretty cool now these are just labels they don't really - do anything in terms of sequence these help us identify which we all have - has been applied to which sequence and that's all we do so other than that - it is the same notation so the fraction such as this one means that there - is a sequence in the denominator which we will prove if there are proofs - given for sequence in the numerator in this rule there are two sequence - of them in the numerator other rules may have one sequence in the numerator - or no sequence in the numerator so these rules that will have no previous - sequence required those are axioms this axiom means if you have an atomic - X in other words it's a variable it's a type variables not not a complicated - expression just attack variable and you can derive that same variable this - is our accion right here now why is it important that this is atomic that - this is type variable and not a more complicated expression actually not - important but it's the simplest rule that you can come up with and mathematicia -ns always like the most minimal set of rules so that's why they say let's - only consider this rule for the type variables X not for more complicated - expressions but we can consider this rule for any expression of course - the identity axiom well here is a truth truth axiom net which derives the - truth which is the ste symbol which I denote it by one the format in logical - notation this is the T symbol well let's just call this one for clarity - so that can be derived from any premises with no previous sequence necessary - none of these other rules now what do these other rules do they do an interesti -ng thing actually each of these rules is either about something in the sequence - on the left to the trans time or something in the sequence to the right - of the transplant which I here shown in blue so these are the interesting - parts of the sequence that are being worked on or transformed by the rule - so here's an example this rule is actually two rules the eyes the index - so I is one or two another two rules just written for gravity like this - with index I and each of them says you will prove this if you prove one - of if you prove this so for example you will prove C given if you're given - a a one A two if you will prove C given just a one which makes sense because - if you can prove C given a one you don't need a two we can ignore this - a T we can already proved C from anyone so in this way it would be proved - and so all these rules work in this way you can prove what's on the bottom - of the seat of the of the fraction if you're given proofs for what's on - the top so these are eight derivation rules and two axioms we can use this - now to make a proof search how do we do that I start with a sequence we - see which rule matches that sequence so the sequence must have something - on the left and something on the right well at least one of these it cannot - be empty so it must be something somewhere and there are only four kinds - of expressions in our logic type variables conjunctions implications and - disjunctions now notice I'm using this arithmetic arithmetic all notation - for logic just because I like it better and I will show that it has advantages - later so we take a sequence we see which rule matches one of them won't - match because either in the premise we have one of these expressions were - in the goal we have one of these expressions and then we find the rule - of match that matches we apply that rule so we now have new sequence one - or more that we will need to be proved and if they're true then we fork - the tree and now we have to prove both of them son-in we continue doing - that for each of the sequence until we hit axioms so the tree will and - this leaf or we hit a sequence to which no rule applies in which case we - cannot prove it and the entire thing is unprovable so in the search tree - there will be sequence at the nodes of the tree and proofs will be at the - edges of the tree so each node sends its proof to the root of the tree - this calculus is guaranteed by mathematicians to be such that indeed if - you cannot find a rule that applies that means the sequence cannot be proved - which was not the case here the sequence can be proved and yet we cannot - find a rule that applies so in this calculus we can use bottom-up approach - to make a proof search as a tree here we cannot that is the advantage capitaliz -ing on the mathematicians results let us look at an example suppose we want - to prove this formula this theorem so first step we need to write a sequence - and this needs to be proved from no premises so we write a sequence s0 - which has an empty set of premises this is a single now what rule applies - to this sequence with your bottom up so in other words we look at these - rules and they refine which denominator matches our sequential and our - cylinders empty set on the left so all the rules on the left cannot be - applied but on the right we have an expression which is an implication - at the top level of this expression there is this implies that so this - is of the form a implies B so this rule applies we have a sequence of the - form something in our case this is an empty set and then a implies B so - we apply this rule which is the right implication and we get a new sequence - which is that what was here before the implication is now put on the left - to the trans of the to the left of the trans time and it means that this - expression needs to be now to the left of the turnstile so now this is - the sequence s1 now we need to prove s1 well we see what rule applies to - us one well on the right there is just Q so nothing can be done of these - rules and Q is not truth so we cannot use the axiom either so let's look - at their left rules on the Left we have now an implication so this is let's - say a and this is B so we have a rule which has a implication B on the - left this is the row left implication let's apply it that law will give - us two new sequence so these two new sequence are s2 and s3 no these ones - as you can check if you match a location B against this implication Q so - this is a this is B so then you get these two sequence now we have to prove - these two sequence as 2 and s 3 s 3 is easy it is just the axiom of identity - it is this now as 2 again has an implication on the left let's again apply - the rule left implication to that we get two more sequence as foreign s5 - as for is this because 5 is this so now actually we are in trouble because - as 2 and s 4 is are the same sequence as 5 actually we could prove with - some more work but that won't help because we are in a situation when to - prove as two we need to prove again s 2 so that's it that's a loop that - will never give us anything it means we applied the wrong rule so we need - to backtrack this step when we apply the rule left implication to s 2 we - erase is 4 in this 5 and try a different rule to apply to s 2 which rule - can apply to s 2 well as to is this it actually has implication on the - right so we can use the right implication rule and if we do that we get - a sequence s 6 which is this and this sequence immediately follows from - the identity axiom because it has promise are on the left and premise are - and goal are on the right and that is this axiom whatever other premises - and the premise X on the left premise X on the right and that is a type - variable so that's perfect we have done the proof as 6 follows from the - axiom and therefore we have proved s0 no more sequins need to be proved - and because sequence s0 shows this to be derived from no premises than - this formula is the theorem that's what the theorem means in the logic - so that is how we use this calculus to do proof search now we notice that - we were a bit stuck at some point we had a loop now if we are in the loop - we don't know what to do maybe we need to continue applying the same rule - maybe some new sequence come up or maybe we should not continue it is not - clear what to do and just looking at the rule left implication shows us - that it's copying this premise a implication B it is copied into the premises - of the new sequence and so it will generate a loop assuredly after the - second time you apply it however this sequence might be new so we might - need to apply it second time we don't know that so that is a problem it - will do now there have been a lot of work trying to fix this problem and - literally decades from research by mathematicians the main ones I found - were what are the off we published in the Soviet Union who de Meyer and - dick Hoff who published in the United States over this time discovered - gradually a new set of rules which is called ljt or the calculus ljt which - cures this problem of looping the way it clears this problem is by replacing - this rule left implication through four new rules which are listed here - all other rules are kept the same from this calculus except the rule left - implication which is replaced in what way so left implication was applying - it applied to a sequence when the sequin had an implication among the premises - or on the left to the left of the turnstile the new rules look in more - detail at what is that implication so that implication could have one of - the four expressions as the argument of the implication it could have an - atomic expression as the argument it would have a conjunction as the argument - could have a disjunction as the argument or it could have an implication - as the argument in our logic there are no more expressions except these - four atomic variables conjunctions disjunction and implications and so - we have here enumerated all the possibilities for what could be to the - left of the implication in this premise which I have here shown in the - blue in blue and so for each of these we do certain things replacing this - sequence with one or more other sequence again it's quite a lot of work - to prove that these rules are equivalent to these and also that the new - rules are somehow better they are not giving loops a lot of work which - I am NOT going to go through because that's far too complicated for the - scope so what we need suffice it to say that we have very smart people - who published on this and it is reasonably sure that this is correct so - the T in the name lgt starts stands for terminating so if we use these - rules in the same way by by creating a proof tree the proof tree will have - no loops and will terminate after a finite number of steps and there is - actually this paper that is also helpful for understanding how to implement - this algorithm and this paper shows explicitly how to construct an integer - function from sequence to integers which is a measure of the complexity - of the sequence and this measure decreases every time you apply a rule - so it strictly decreases and since this is a strictly decreasing measure - on the proof tree it means that all the next nodes in the proof tree will - have a smaller value of this measure so eventually it will hit zero and - the proof tree will terminate at that leaf either that or you have no more - rules to apply and if you have no more laws to apply then again mathematicians - have proved it means our sequence cannot be proved so this is an important - result that we are going to use and note that this this rule is quite complicat -ed it does a very interesting thing it takes this expression which has implicati -on inside an implication and it transforms this expression in a weird way - namely the B here is separated from the C by parenthesis but here it is - not separated so this transformation is highly non-trivial and unexpected - and its validity is based on this theorem that this in the intuitionistic - logic is equivalent to this equivalent means they're both following from - the other so from this promos that and from there follows this so this - key theorem was attributed to rob you off my dick off in this paper and - this is this lemma 2 which says that if this sorry that the this derivation - is if and only if that derivations will have these two equivalences and - the proof is trivial and the 34 is a reference to to borrow be off now - when a mathematician says that something is trivial doesn't mean that a - statement is easy to understand it doesn't mean that the proof is easy - to find or that it has trees easy to understand it means none of these - things it just means that right now for this mathematician it is not interestin -g to talk about how it is done that's all it means could be for any number - of reasons for example mathematicians could just be lazy or have no time - to again explain this and so they say it's trivial don't be don't be deceived - when you see somebody says that something is trivial in a mathematical - text so to prove this one stepping stone could be to prove this first this - is an easier theorem and if you prove this then clearly from here you can - get B to C B to C you can substitute in here you can get a to B and then - you have here a to B so in this way you can show this equivalence in one - direction now the proof of this statement is obviously trivial in order - to show the expression of this type I will use my short notation so this - is F which has this type the first argument of the function the second - is B which is at this type then we need to produce a see how do we produce - a C we apply F to an argument of this type the argument of this type is - a function that takes a and returns a B so we take some X of type a and - we return a B which was this B so we ignore this X we just returned that - B and that's the argument of F so this expression is the proof of this - sequence in other words this is the code that has this type and therefore - the proof must be available somehow so the details of proving this theorem - are left as an exercise for the reader again when you see in a mathematical - text that something is left as an exercise for the reader it does not mean - that it is easy to do it does not mean that for you it would be a useful - exercise to do it also does not mean that the author knows how to do it - it means none of these things it just means the author doesn't feel like - doing it right now and showing it to you for whatever reason could be because - they are lazy it could be because I don't know how to do it could be because - they feel that they should know how to do it but they don't really do know - how to do it could be any of these reasons don't be deceived when you see - something like this but of course I had to actually produce an expression - function of this type in order to implement my curry forward language because - as I will show in a moment we need to be able to implement all these has - code in order to help approver so why is that we believe the mathematicians - that the new rules are equivalent to the old rules which means that if - you find a proof using these rules somehow you should be able to find the - proof also using our initial rules which means that if you found that proof - it would easily translate that to code because each step here is directly - corresponding to a certain code expression as we have seen at the beginning - of this tutorial these cold expressions from each of these operations so - in order to do this with new rules in other words in order to create code - from proof using new rules we need to show equivalence or we need to show - how to get code out of each of the new rules now proof of a sequence means - that we have some expression let's say T what uses variables a B and C - of these types and expression itself has type G and also as I have shown - this could be conveniently seen as a function the T as a function from - a B and C from these three arguments to the type G so for each sequencing - a proof we should be able to show either that it follows from an axiom - one of these or that it show it follows from a derivation rule and the - derivations all transforms one proof into another the axioms are just fixed - expressions as we had before the axiom that actually didn't change between - our initial formulation of the logic and the new calculus lgt they actually - did not change the derivation rules changed each new derivation rule means - that you're given expressions that prove the sequence in the numerator - one or more and you are out of these expressions somehow you have to construct - an expression that proves this sequence now when I say an expression proves - the sequence what it means is that expression has the type that is described - by the sequence it's the same thing because we described types of expressions - through sequence and only those sequence that correspond to valid and existing - expressions in the programming language only those sequence can be proved - by the logic this is by construction so now we need to just find what are - these expressions that corresponds to each of the derivation rules in each - rule has a proof transformer function as I call it and the proof transfer - function is explicitly a function that takes one or more expressions that - are in the numerator and converts that to the expression in the denominator - that has this type so it has an expression as it has an explicit function - we need to write down for each of the derivation rules so let's see how - this is done for these two examples of derivation laws first example have - a rule that says if you want to derive this sequence we need to derive - these two sequence now this sequence represents an expression of type C - which uses an expression of type A plus B so let's represent this as a - function from a plus B to C now we will be able to just ignore these other - premises which are common arguments and all these functions we just pass - them and we don't write them out what is the proof transformer for this - derivation rule the proof transformer for it is a function that has two - arguments t1 which is the proof of this must be a function of type II to - see and t2 which is a proof of this sequence which must be a function of - type B to see now earlier I said that sequence represent expressions that - use certain variables but equivalently we can say these are functions that - take these variables and return these expressions that's more convenient - when you implement this in code so what we need is a function that takes - a to C and B to C and returns a function from a plus B to C and this is - the code that does it we take an argument of type a plus B and we return - a match expression if it's in the left we applied t1 to that value and - we get to see if it's in the right we apply t2 to that value and we get - a C so in any case we get a syllabus so this is a function from a plus - B to C as required another example is the proof transformer for this rule - this rule has one sequence going to one sequence so in order to transform - is proof into this we need a function that takes argument of type A to - B to C to D and returns a function of type tuple a B going to C to D so - here's the code we take a function f of type A to B to C to D we return - a function that takes a G of this type shown here in blue and return we - need to return a D so how do we get a deal we apply F to a function of - type A to B to C so we create that function out of G X of type a going - to Y of type B going to G of x1 so this is a function of type A to B to - C which is the argument of F as required and the result is of type D so - that is what we write so this kind of code is the proof transformer for - this derivation arrow and we need to produce this proof transformers for - every rule of the calculus lgt and I have done it because I have implemented - the Korea Howard library that uses LG T so I'll must have done it for each - flow this is a bit tedious because there are many of those rules and you - need to implement all this machinery of passing arguments no matter how - many in this gamma which are emitted from this notation for brevity but - in of course in the real code you have to deal with all that too so let's - see how this works on an example because once the proof tree is found we - need to start backwards from the leaves of the tree back to the root on - each step we take the proof expression apply the proof transformer to ative - according to the rule that was used on that step we get a new proof expression - and so on so for each sequence we will get a proof expression and at the - end we'll have a proof expression for the root sequence and that will be - the answer so I will denote denote by T I the proof expressions for the - sequence s hi so starting from s6 s6 was this sequence in our proof so - I mean yes just just going through the proof example it was here backwards - from a 6 back to a 0 s-six was this it followed from axiom identity it's - proof expression t6 is a function of two variables these two variables - of these two types and this function just returns the second variable so - it's a function of RR q and r and just denote this by our argued and Garibaldi' -s types r RQ variable of this type is hard here so this function is very - simple just ignores the first argument and returns or so that is what the - axiom does the next sequence was as to as to was obtained by rule our implicati -on or right implication from s 6 so the proof transformer for right implication - let's look at the right implication and see what the proof transformer - must be so we are given this sequence for this expression which is the - function body the function body that uses a variable of type a somehow - out of this we need to produce a function expression that takes an argument - of type a and returns that functional body so this is the code which is - just writing a new argument returning the function body that was our proof - transformer we need to convert function body into a function so we just - write that argument and arrow in the function body so in our case we need - this as a function body and so our t2 is a function of our Q and this function - is this the sequence s 3 followed from the axiom and so it was just this - function this is just the identity function then we used the left implication - so this was actually still done in the calculus algae but the same thing - works in the calculus lgt I'm just using algae because it's simpler for - example here proof transformer for the left implication is a little more - complicated and so if you look at it what what does it have to be it takes - these two expressions and returns this expression so it takes a function - from A to B to a and from B to C and it returns a function from A to B - to see how does it do it given a function a to b you use this to derive - a from it then you substitute that a into the function into B you get a - B when you use this to derive see from that B and that's your C so you - use this function a to be twice you put it in here once and then you get - an A and substitute back into the same function when you get a B then you - use that and that's exactly what the proof transformer does it takes this - rrq and it uses it twice substitutes into it something that was obtained - from one of the terms and then uses the second term on the result so then - this is the proof transformer for the rule left implication the result - of the proof transformation is the proof for the sequence s1 finally we - use the right implication again which is just this function construction - and we get the proof expression for the sequence s0 now this proof expression - is written through these t1 t2 t3 we have to substitute all this back in - order to get the final expression so if we substitute first of all we find - this is our our cubone going to tea one of our cutie one of our queue is - this so we have to put it here now t3 is just identity so we can just remove - that so that gets you riq going to our Q of T 2 T 2 is less if I have to - put it in T 6 is just identity on R so this is our going to our and so - finally you have this expression so that is the final code that has the - required type notice that we have derived this code completely algorithmic - to it there was no guessing we found which rules applied to the sequence - with transformed sequence according to the rules once we found the proof - which was if we use the calculus ljt the proof will be just a finite tree - with no loops it will terminate you can get an exhaustive depth-first search - for it for example and you find all the possible proofs if you want as - well well you will find many in any case in some for some expressions and - then we use the proof transformers which are fixed functions that you can - upfront compute for each these expressions are proof transformers applied - to the previous proofs so these are completely fixed algorithmically fixed - so we have derived this code completely algorithmically given this expression - this type so it is in this way that the career Howard correspondence allows - us to derive the code of functions from there type signatures another important - application of the correspondence is to analyze type by some morphisms - or type equivalences and I was led to this by asking the question so in - this logic or in the types are these operations plus and times as I denoted - them more like logic more like the disjunction and conjunction or are they - more like arithmetic plus and times because this is kind of not so clear - right away our logic is this intuitionistic logic it in any case this is - different from boolean logic so what are the properties of these types - really so are the properties such that it is better to think about these - operations as plus and times rather than logical conjunction and disjunction - can answer this question I looked at identities that we have in the water - these are some identities from simple ones obvious ones to less obvious - identities like this the equal sign here stands for implication in both - directions so both this implies that and vice versa because of this each - of the implications means a function so since these are all identities - in logic it means that for example the implication from here to here is - a theorem of logic and so it can be implemented as we know all our identities - in logic can be implemented in code and we even have an algorithm now that - can automatically produce proofs and automatically produce code so that - means for any of these identities that has some ik some expression X on - the left and some Y on the right so some kind of X equals y we have X implies - Y and y implies X if we convert that to code we will have a pair of functions - function from X to one and the function from Y to X what do these functions - do well they convert values in some ways from type X to type Y and back - so do these functions Express the equivalence of the types x and y so that - any value of type X can be converted to some equivalent value type while - and back without any loss of information is that so that was the question - I asked I looked at some examples well first what does it mean more rigorously - that types are equivalent for as mathematicians say isomorphic the types - are isomorphic and we will use this notation for that if there is a one-to-one - correspondence between the sets of values of these types and in order to - demonstrate that we need a pair of functions one going from A to B the - other going from B to a such that the composition of these functions in - both directions is equal to identity function so F compose G or F value - G will give you from A to B and then from B to a is back so that would - be identity of a to a this will be identity of B to B if this is true if - the composition is identity it means we indeed did not lose any information - let's consider an example this is an identity in the logic a conjunction - with one is equal to a in Scala the types responding to the left and the - right hand sides of this conjunction all of this are equivalent are the - conjunction of a and unit and a itself now we need functions with these - types indeed we can write functions is having these types a pair of a and - unit we need to produce an a out of that we'll just take the first element - of the pair you are done take an X of type a will produce tuple of a and - unit very easy just put a unit value in the tuple in here done and it's - easy to verify that composition of these functions will not change any - values so it will be identity in both directions another example this is - an identity in logic if this is understood as a disjunction one or a or - true or a is true that is an identity in logic for theorem in the logic - are the types equivalent though the type for 1 plus a is the option in - Scala it is option in Haskell at is called maybe this type is standard - library type in pretty much every functional programming language now option - of a is a disjunction of one or unit and a it is certainly not equivalent - to just unit because this type could contain a value of a in it but this - could not so there is no way that you could transform this type to this - and then back without losing information you could transform so since this - is a theorem you have functions from this type to this type and back some - functions you have them but these functions do not compose to identity - they cannot because what if you had a here you must map it into unit from - this unit back you must map into this unit you cannot get an a out of unit - and so that will erase this information and that cannot become isomorphism - so we see that some logic identities do yield isomorphism types but others - do not why is that let's look at some more examples to figure out why in - all these examples we can implement functions F 1 and F 2 between the two - sets to two types in both directions and then we can check we certainly - can implement them because these are logical identities but then we can - check if the compositions are identity functions and if so the types are - isomorphic but we find that in the first three examples we can do it but - in this last example we can note now I have written the logical identities - logical theorems with the arithmetic notation I call this arithmetical - notation because this suggests arithmetic operations plus and times and - if you look at these identities this looks like a well-known algebraic - identity from the school algebra in this too but this certainly seen your - own as an arithmetic as an as an arithmetic identity this is certainly - not true in arithmetic it is true in logical if you replace this with disjuncti -on and this with conjunction this is an identity in logic so this suggests - an interesting thing if you replace disjunction by plus and conjunction - by x and the result is an identity in arithmetic then it is an isomorphism - of types otherwise it is not let's see why this is so indeed this is so - I call this the arithmetic arithmetic oh very hard correspondence to see - how it works let's consider only the types without loss of generation of - generality that have a finite set of possible values for example a boolean - type has only two possible true and false integer let's say in the computers - all the integers are fine nights ago so those types have a finite set of - possible values and this does not limit our generality because in the computer - everything is finite all types have a finite set of possible values now - let's consider how many values a given type has so that would be the size - of the type or using the mathematical terminology it's called a cardinality - of the type so let's see what is the cardinality of various type constructions - the sum type for example if the cardinality of types a and B is known and - the cardinality of a plus B the sum type the disjunction of a and B is - the sum of the two cardinalities or sizes this is because a value of the - disjunction type is constructed as either a value of the first part or - a value of the second part and so you cannot have both together and so - obviously the different number of values is just the sum of the two sizes - that the number of different values of the sum type is just the sum of - the numbers of different values of types a and B for the product type again - we have an interesting thing it's the arithmetic product of the sizes of - a and B because for every a value you could have an arbitrary B value so - this is a direct product or transient product of sets and we have school - level identities about the operations plus and times such as these identities - or these all of these identities are valid for arithmetic and they show - if you translate that into statements about the sizes of types they show - that the size of the type on the left is equal to the size of the type - on the right and that is very suggestive in other words if you take a identity - like this and you compute the size of the type on the left and the size - of the type on the right you get an arithmetic identity of the sizes but - you don't get that identity here because the earth medical formula is not - right this is very suggestive if the sizes are equal and maybe the types - are equivalent or isomorphic when the sizes are not equal then certainly - they cannot be equivalent the function type very interestingly also is - described in the same way it provides the set of all maps between the two - sets of values so for example from integer to boolean that would be all - the functions that take some integer and return some boolean so that's - and a number of boolean values ^ the number of integer values that's how - many different functions you can have as a combinatorial number so it's - an exponential and so the size of the type of function a to be is the size - of the type of B ^ the size of type of a and again we have all the school - identities about powers and how to multiply powers and so on and they are - directly translated into these three identities if you take the sizes of - the types on the left and on the right the sizes will be equal due to these - three identities since the sizes are equal it's very likely that the type - our actual equivalent so far haven't seen any counter examples to this - in these constructions so this gives us a meaning of the Curie Howard correspon -dence so far we have seen three facets of the curly Howard correspondence - one is the correspondence between types and logical formulas two is the - correspondence between code and proofs and three the correspondence between - the cardinality of a type or the set size of the type and the arithmetic - identities that we have in the school algebra about these types so arithmetical - identities signify type equivalence or isomorphism while logic identities - only talk about how you create some value of this type out of value of - another type so that does not guarantee that it preserves information it - just guarantees that you can implement some function of that type it doesn't - tell you that the function will be an isomorphism so if one type is logically - equivalent to another it means are equally implementable if one is implementabl -e another is also implementable but no more than that whereas arithmetical - identities actually tell you about isomorphism of types therefore if you - look at types and write them using my preferred notation which is using - the arithmetic all symbols instead of logical symbols instead of these - I'll use these symbols if I do that this is very suggestive of a possible - isomorphism of types then it becomes very easy for me to reason about types - I can see right away that these two are isomorphic types or that these - two are isomorphic types because I am used to looking at school algebra - it's very obvious then that this is not an isomorphism of types because - this doesn't make sense in the school algebra so reasoning about isomorphic - types is basically school level algebra involving polynomials and powers - so if you are familiar with all these identities as you should be it will - be very easy for you the reason about what types are equivalent as long - as all these types are made up of constants or primitive types disjunctions - tuples or conjunctions and functions which will then directly be translated - into exponential polynomial expressions constants sums products and expand - powers or Exponential's so I call these exponential polynomial types that - is types built up from these type constructions so all we have been talking - about in this tutorial is what I call exponential polynomial types these - are the basic type constructions that I started with tuple product function - exponential disjunction some unit constant or 1 now just one comment that - in the functional programming community today there is a terminology algebraic - types so people usually call algebraic types the types that are made from - constant types sums and products excluding Exponential's I do not find - this terminology it's very helpful I find it confusing because what is - particularly an algebraic about these identities these are identities of - school algebra the properties of the function type are described by algebraic - identities like this so it would be strange to call the function type not - algebraic whereas these types are algebraic they are very similar to each - other in terms of their properties being described by identity is known - from school algebra so instead of algebraic types I would prefer to say - polynomial types this is much more descriptive and precise and if you want - to talk about function types as well then you just can you can just say - exponential polynomial types or exfoli types for short so by way of summarizing - what we have done so far what are the practical implications of the career - Howard correspondence so one set of implications is actually for writing - code and reason and eternal code one thing we can do now is if we're given - a function with some type and usually this will be typed with type parameters - all type trainers fully parametric types such as the function we have been - considering here all these functions do not have any types that are specific - like integer or string all the types are fully parametric and then there - are some constructions some type expressions made out of these types so - these are what I call fully parametric functions for these functions we - have a decision procedure an algorithm that based on the ljt calculus which - decides whether this function can be implemented in code and computer scientist -s a type is inhabited if you can produce a value of this type in your program - so CH of T is this proposition which they call type is inhabited and I - prefer to call it just that you can compute a value of this type or code - has the type O code can create a value of this type and so we have a algorithm - that can also generate the code from type when it is possible if it is - not possible the algorithm will tell you so often not always but often - this algorithm can be used actually to generate the code you want we can - also use what I call the arithmetic of glory Harvard correspondence to - reason about type isomorphisms and to transform types isomorphic we simplify - type expressions just like we simplify expressions in school level algebra - by expanding brackets by permuting the order of terms like a plus B is - equal to B plus a or associativity a times B all times C can be expanded - and so on so this allows us once we have written types in the short notation - in the notation that I prefer which resembles school algebra because it - uses the plus and times symbols instead of the logic symbols so once we - rewrite our types and this notation which I have been doing consistently - in this tutorial it enables us the reason very easily but which types are - equal or isomorphic because we are all familiar with the school level algebra - what are the problems that we cannot solve using this knowledge one thing - we cannot do is to generate code automatically such that it will be an - isomorphism so for instance in an example here we are able to generate - automatically the code of these functions but it will not be an isomorphism - and the lgt algorithm cannot check that this is nice a morphism that's - the important thing this algorithm does not know about equations or isomorphism -s it only knows that it found some code that has the type you wanted whether - this code is useful to you or not we don't know the algorithm doesn't know - this also if the algorithm finds several such several proofs of a sequence - it will generate several not in equivalent versions of your code it doesn't - know which one is is useful maybe some of them are useless maybe not the - algorithm cannot automatically decide that in general another thing we - cannot do is to express complicated conditions via types such as that array - is sorted the type system is not powerful enough in all the languages I - listed you need a much more powerful type system such as that in the programmin -g language interests or add them or cook those are much more powerful type - systems that can express such complicated conditions but for those type - systems there is no algorithm that will generate code another thing we - cannot do is to generate code that has type constructors such as the map - function here's an example in Scala this is a map function on a list so - there's the list of a a is a type parameter and then we say dot map and - map has another type frame to be it takes a function from A to B for any - B so a is fixed but now from any B we can take a function from A to B and - generate a list of B so if we wrote this formula in the short type notation - this would look something like this I'm writing subscript a because this - is a type parameter so this is like an argument or a type parameter I'm - writing it like this and then from this this is the first argument of the - function and then there is a second argument which is this F and that is - another quantifier for B inside parentheses so this formula has a quantifier - inside so far we have been dealing with formulas that have all quantifiers - outside and so we never write quantifiers explicitly but here we have to - write them inside this is a more powerful logic which is called first-order - logic in other words this is a logic where you have quantifiers anywhere - in the formula including inside the formula unfortunately this logic is - undecidable so there is no algorithm that we can use either to find the - proof and therefore code freedom type or to show that there is no proof - no code so we're kind of stuck in all these directions some more remarks - about the curry Harvard correspondence first is that only with parameterize - types we can get some interesting information out of it if we take concrete - types like integer then the proposition CH event meaning that our code - can have a value of type int it that's always true can always write any - some integer value we don't need any previous data for it so for all specific - types all these propositions are always choice completely void of information - the only interesting part comes when we start considering type variables - if we start asking can we make a type which is either of a B going to a - going to B in soon for all a B once we start doing this with type parameters - a B and so on then we get interesting information as we have seen in this - tutorial another remark is that functions like this one are not sufficiently - described by their type so that this is the type of integer going to integer - now looking at this type we can put this into a sequence but we'll never - get enough information to actually get this function so only certain class - of functions which are fully typed biometric their type signature is informativ -e enough so that we can derive code automatically only in much more powerful - type systems you can have type information that is enough to specify fully - a code like this another caveat is that I don't know the proof that arithmetic - identity guarantees the type equivalence it is certainly a necessary condition - because if two types have different cardinality or different size of their - sets of values that they cannot be equivalent or they cannot be isomorphic - so this is a necessary condition but it's not a sufficient condition it - looks like I don't know if this is sufficient I haven't seen any counter - examples so far final remarks about type correspondence the logical constant - false did not appear in any of my slides so far this was on purpose it - has extremely limited practical use in programming languages because actually - we have types corresponding to false Scala has type called nothing Haskell - has type usually called void that corresponds to the logical constant false - what does it mean CH of nothing is false it means your code can never have - a value of type nothing or in Haskell void you can never compute a value - of this type so clearly it has a very limited practical significance you - will never be able to compute any values of this type ever in any program - it's identically falseness this constant so if you want to add it to the - logic it's very easy you just have one rule and you're not done you can - derive things with it if you want but they will have almost never any use - in practical code also we did not talk about negation none of the calculus - calculate that I should have in logical negation as in operation again - for the same reason we do not have a programming language construction - that represents logical negation negation by definition is like this is - an application from 8 to 4 so that's not a not a means from a follows falsehood - now since you cannot ever get false in a programming language you cannot - really implement this function in any useful sense and so i have seen some - haskell library that used this type void as a type parameter in some way - but certainly it's a very limited and rare use and so it is not really - lumen 18 to include negation it could probably find some very esoteric - uses of it but almost never useful and finally there is another set of - important implications from the Kurihara correspondence these are implications - for people who want to design new programming languages as we have seen - the Karaka with correspondence maps the type system of a programming language - into a certain logical system where prepositions follow from each other - or can be proved from each other and this enables us to reason about programmed - to see what kind of code can be written if some other kind of code can - be written and logical reasoning is very powerful it's simpler than trying - to write code and it gives you algorithms and all kinds of mathematical - results that have been found over the centuries so languages like those - listed here have all the five type constructions that I wasted in the beginning - of this tutorial and mapping them into logic gives a full constructive - logic or full intuitionistic logic with all logical operations and or so - conjunction disjunction implication and the truth constant whereas languages - such as C C++ Java and c-sharp and so on they're mapped to incomplete logics - because they do not have some of these operations for instance they do - not have type constructions of correspond to disjunction we also do not - have the true constant or the false constant so they are mapped to a logic - that lacks some of the foundational logical operation so it can be only - fewer theorems can be proved in that logic and so your reasoning about - theory types is hampered languages called scripting languages sometimes - such as Python or JavaScript will be and so on also our belongs there in - that line those languages only have one type they actually don't check - types at compile time and so they're mapped to logics with only one proposition - those logics are extremely small in terms of what kind of things you can - reason about and so if you write a program in these languages you are completel -y unable to reason at the level of types whereas in these languages you - are able to reason but in a limited way you're not having a complete logic - so this suggests a principle for designing the type system in a new programming - language the first step would be to choose a good and complete logic that - is free of inconsistency mathematicians have studied all kinds of logics - and they are always interested in questions such as is this logic consistent - consistent means you cannot derive false from true is this logic complete - can you derive all things that are true are there enough axioms and rules - of derivation or maybe there are too many axioms and rules of derivation - you can delete some of them and have fewer mathematicians have always been - interested in such questions they found all kinds of interesting logics - where you can derive a lot of interesting theorems non trivial theorems - and they found the minimum sets of axioms and rules of derivations for - these logics use their results take one of the logics that they do them - and develop such as intuitionistic logic model logic temporal logic linear - logic and so on take one of these logics for each of the basic operations - of this logic provide type constructions in your programming language that - are easy to use for instance your logic has disjunction implication or - something else provide a type constructor for each of them that's easy - to use easy to write down such as provided by the languages we have seen - then every type will be mapped to a logical form of the OPF logical formula - for every type and there will be a type for every logical formula and then - for each rule of the new logic for each derivation rule there should be - a construct in the code that corresponds to it so that you could transform - proofs in logic into code and code into proofs if you do that your language - will be faithful to the scorecard correspondence you will be able to use - logic to reason about your language and one important result at this level - while we have seen that you can sometimes generate code that is maybe nice - but a very important result is that if your logic is free of inconsistency - it means that no program will ever be able to derive an inconsistent an - inconsistent type means that you had a function that requires some type - a but it was called with a different type beam which is incompatible and - that basically crashes so in languages like C and C++ we have all kinds - of crashes like a segmentation fault in Java the exceptions nullpointerexceptio -n or class cast exception which happens when you call a function on the - wrong type of argument and that happens if your logic is inconsistent if - your logic can derive incorrect statements from correct premises then if - you translate that derivation into code and the that code will derive incompati -ble type at the wrong place and it will crash the crash will happen at runtime - the compiler will not catch this inconsistency because the compiler only - checks the logic of types and the logic checks out you have followed the - rules of derivation of the logic the compiler can check out all these logical - rules but the compiler does not know that your logic is inconsistent maybe - and then it will deep have derived an inconsistent result falsehood from - truth for example and that will crash at runtime now we know that crashing - at runtime is not a good outcome so in fact languages like Oh camel have - been studied and for other languages some subsets of Haskell I believe - called safe Haskell have been studied and it has been shown that they cannot - crash and they're the way to show it mathematically is to use the fact - that they are based on a complete and consistent logic and then all you - need to show is that your compiler does not have some critical bugs that - allow it to oversee that you have not followed the derivation rules of - the logic that is an extremely valuable feature of functional programming - languages that are based on the Curie habit correspondence you can prove - their safety at compile time or at least exclude a large number of possible - bugs and errors certainly these languages are quite large and they include - features that are not covered by the Carey Hart correspondence type constructor -s that I have not considered in this tutorial and those might may not be - safe but at least the foundation of these languages the foundation of the - type system will be safe so that is the final lesson from the great Howard - correspondence this concludes the tutorial + +Int => Int +\end_layout + +\end_inset + +, may be implemented by many other functions, such as +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +x => x - 1 +\end_layout + +\end_inset + +, +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +x => x * 2 +\end_layout + +\end_inset + +, etc. + So, the type signature +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Int => Int \end_layout \end_inset - + is insufficient to specify the code of the function, and deriving code + from that type is not a meaningful task. + Only a fully parametric type signature, such as +\begin_inset Formula $A\rightarrow\left(A\rightarrow B\right)\rightarrow B$ +\end_inset + +, could give enough information for deriving the function's code. + Additionally, we must require the code of functions to be fully parametric. + Otherwise we will be unable to reason about code derivation from type signature +s. \end_layout -\begin_layout Subsection -The LJT algorithm -\begin_inset CommandInset label -LatexCommand label -name "app:The-LJT-algorithm" +\begin_layout Standard +Validity of a +\begin_inset Formula ${\cal CH}$ +\end_inset +-proposition +\begin_inset Formula ${\cal CH}(T)$ \end_inset + means that we can implement +\emph on +some +\emph default + value of the given type +\begin_inset Formula $T$ +\end_inset +. + But this does not give any information about the properties of that value, + such as whether it satisfies any laws. + This is why type equivalence (which requires the laws of isomorphisms) + is not determined by an equivalence of logical formulas. \end_layout \begin_layout Standard -In this chapter, we have seen how to derive code from a type signature as - long as one has a proof of the sequent corresponding to that type signature. - But Section -\begin_inset space ~ +It is useful for programmers to be able to transform type expressions to + equivalent simpler types before starting to write code. + The type notation introduced in this book is designed to help programmers + to recognize patterns in type expressions and to reason about them more + easily. + We have shown that a type equivalence corresponds to +\emph on +each +\emph default + standard arithmetic identity such as +\begin_inset Formula $\left(a+b\right)+c=a+\left(b+c\right)$ \end_inset - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Example:-Proving-a-ch-proposition" -plural "false" -caps "false" -noprefix "false" - +, +\begin_inset Formula $\left(a\times b\right)\times c=a\times(b\times c)$ \end_inset - gave an example showing that the rules in Table -\begin_inset space ~ +, +\begin_inset Formula $1\times a=a$ \end_inset +, +\begin_inset Formula $\left(a+b\right)\times c=a\times c+b\times c$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Proof-rules-of-constructive-and-boolean" -plural "false" -caps "false" -noprefix "false" +, and so on. + Because of this, we are allowed to transform and simplify types as if they + were arithmetic expressions, e.g., to rewrite: +\begin_inset Formula +\[ +\bbnum 1\times\left(A+B\right)\times C+D\cong D+A\times C+B\times C\quad. +\] \end_inset - do not provide an algorithm for finding a proof for a given sequent. +The type notation makes this reasoning more intuitive (for people familiar + with arithmetic). + \end_layout \begin_layout Standard -To illustrate this problem on another example, let us try proving the sequent: -\begin_inset Formula -\[ -A,B\vee C\vdash(A\wedge B)\vee C\quad. -\] +These results apply to all type expressions built up using product types, + disjunctive types (also called +\begin_inset Quotes eld +\end_inset +sum +\begin_inset Quotes erd \end_inset -We expect that this sequent is provable because we can write the corresponding - Scala code: -\begin_inset listings -inline false + types because they correspond to arithmetic sums), and function types (also + called +\begin_inset Quotes eld +\end_inset + +exponential +\begin_inset Quotes erd +\end_inset + + types because they correspond to arithmetic exponentials). + Type expressions that contain only products and sum types are called +\series bold +polynomial +\series default + +\begin_inset Index idx status open \begin_layout Plain Layout - -def f[A, B, C](a: A): Either[B, C] => Either[(A, B), C] = { +polynomial type \end_layout -\begin_layout Plain Layout +\end_inset - case Left(b) => Left((a, b)) -\end_layout -\begin_layout Plain Layout +\begin_inset Index idx +status open - case Right(c) => Right(c) +\begin_layout Plain Layout +types!polynomial types \end_layout +\end_inset + +. +\begin_inset Foot +status open + \begin_layout Plain Layout +These types are often called +\begin_inset Quotes eld +\end_inset -} +algebraic data types +\begin_inset Index idx +status open + +\begin_layout Plain Layout +algebraic data types \end_layout \end_inset -How can we obtain a proof of this sequent according to the rules in Table -\begin_inset space ~ + +\begin_inset Quotes erd \end_inset + but this book prefers the more precise term +\begin_inset Quotes eld +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Proof-rules-of-constructive-and-boolean" -plural "false" -caps "false" -noprefix "false" +polynomial types +\begin_inset Quotes erd +\end_inset + +. +\end_layout \end_inset -? We find that we could potentially apply the rules -\begin_inset Quotes eld + Type expressions that also contain function types are called +\series bold +exponential-polynomial +\series default + +\begin_inset Index idx +status open + +\begin_layout Plain Layout +exponential-polynomial type +\end_layout + \end_inset -create -\begin_inset listings -inline true + +\begin_inset Index idx status open \begin_layout Plain Layout +types!exponential-polynomial types +\end_layout -Left +\end_inset + +. + We focus on exponential-polynomial types because they are sufficient for + almost all design patterns used in functional programming. \end_layout +\begin_layout Standard +There are no types corresponding to subtraction or division, so arithmetic + equations such as: +\begin_inset Formula +\begin{align*} +\left(1-t\right)\times\left(1+t\right) & =1-t\times t\quad,\quad\text{ and }\quad\frac{t+t\times t}{t}=1+t\quad, +\end{align*} + \end_inset +do not directly yield any type equivalences. + However, consider this well-known formula: +\begin_inset Formula +\[ +\frac{1}{1-t}=1+t+t^{2}+t^{3}+...+t^{n}+...\quad. +\] -\begin_inset Quotes erd \end_inset -, -\begin_inset Quotes eld +At first sight, this formula appears to involve subtraction, division, and + an infinite series, and so cannot be directly translated into a type equivalenc +e. + However, the formula can be rewritten as: +\begin_inset Formula +\begin{equation} +\frac{1}{1-t}=L(t)\quad\text{ where }\quad L(t)\triangleq1+t+t^{2}+t^{3}+...+t^{n}\times L(t)\quad.\label{eq:ch-example-type-formula-list} +\end{equation} + \end_inset -create +The definition of +\begin_inset Formula $L(t)$ +\end_inset + + is finite and only contains additions and multiplications. + So, Eq. +\begin_inset space ~ +\end_inset + +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-type-formula-list" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +) can be translated into a type equivalence: +\begin_inset Formula +\begin{equation} +L^{A}\cong1+A+A\times A+A\times A\times A+...+\underbrace{A\times...\times A}_{n\text{ times}}\times\,L^{A}\quad.\label{eq:ch-example-type-expansion-list} +\end{equation} + +\end_inset + +This type formula (with +\begin_inset Formula $n=1$ +\end_inset + +) is equivalent to a recursive definition of the type constructor \begin_inset listings inline true status open \begin_layout Plain Layout -Right +List \end_layout \end_inset +: +\begin_inset Formula +\[ +\text{List}^{A}\triangleq1+A\times\text{List}^{A}\quad. +\] + +\end_inset -\begin_inset Quotes erd +The type equivalence +\begin_inset space ~ \end_inset -, -\begin_inset Quotes eld +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:ch-example-type-expansion-list" +plural "false" +caps "false" +noprefix "false" + \end_inset -use +) suggests that we may view the recursive type \begin_inset listings inline true status open \begin_layout Plain Layout -Either +List \end_layout \end_inset - -\begin_inset Quotes erd -\end_inset - -, and + heuristically as an \begin_inset Quotes eld \end_inset -use function +infinite disjunction \begin_inset Quotes erd \end_inset -. - However, no matter what rule we choose, we will get stuck at the next step. - Let us see why: + describing lists of zero, one, two, etc., elements. +\end_layout + +\begin_layout Subsection +Implications for designing new programming languages \end_layout \begin_layout Standard -To apply -\begin_inset Quotes eld +Today's functional programming practice assumes, at the minimum, that programmer +s will use the six standard type constructions (Section +\begin_inset space ~ \end_inset -create -\begin_inset listings -inline true -status open -\begin_layout Plain Layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Type-notation-and-standard-type-constructions" +plural "false" +caps "false" +noprefix "false" -Left -\end_layout +\end_inset +) and the eight standard code constructions (Section +\begin_inset space ~ \end_inset -\begin_inset Quotes erd -\end_inset +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:The-rules-of-proof" +plural "false" +caps "false" +noprefix "false" -, we first need to prove the sequent -\begin_inset Formula $A,B\vee C\vdash A\wedge B$ \end_inset -. - But this sequent cannot be proved: we do not necessarily have values of - both types -\begin_inset Formula $A$ -\end_inset +). + These constructions are foundational in the sense that they are used to + express all design patterns of functional programming. + A language that does not directly support some of these constructions cannot + be considered a functional programming language. +\end_layout - and -\begin_inset Formula $B$ +\begin_layout Standard +A remarkable result of the CH correspondence is that the type system of + any given programming language (functional or not) is mapped into a +\emph on +certain +\emph default + +\emph on +logic +\emph default +, i.e., a system of logical operations and proof rules. + A logical operation will correspond to each of the +\emph on +type +\emph default + constructions available in the programming language. + A proof rule will correspond to each of the available +\emph on +code +\emph default + constructions. + Programming languages that support all the standard type and code constructions + — for instance, OCaml, Haskell, F#, Scala, Swift, Rust, — are mapped into + the constructive logic with all standard logical operations available ( +\begin_inset Formula $True$ \end_inset - if we are only given values of type -\begin_inset Formula $A$ +, +\begin_inset Formula $False$ \end_inset - and of type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Either[B, C] +, disjunction, conjunction, and implication). \end_layout +\begin_layout Standard +Languages such as C, C++, Java, C#, Go are mapped into logics that do not + have the disjunction operation or the constants +\begin_inset Formula $True$ \end_inset -. - To apply -\begin_inset Quotes eld + and +\begin_inset Formula $False$ \end_inset -create -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Right +. + In other words, these languages are mapped into +\emph on +incomplete +\emph default + logics where some true formulas cannot be proved. + Incompleteness of the logic of types will make a programming language unable + to express certain computations, e.g., directly handle data that belongs + to a disjoint domain. + \end_layout +\begin_layout Standard +Languages that do not enforce type checking (e.g., Python or JavaScript) are + mapped to inconsistent logics where any proposition can be proved — even + propositions normally considered +\begin_inset Formula $False$ \end_inset - -\begin_inset Quotes erd +. + The CH correspondence will map such absurd proofs to code that +\emph on +appears +\emph default + to compute a certain value (since the +\begin_inset Formula $\mathcal{CH}$ \end_inset -, we need to prove the sequent -\begin_inset Formula $A,B\vee C\vdash C$ +-proposition was proved to be +\begin_inset Formula $True$ \end_inset -. - Again, we find that this sequent cannot be proved. - The next choice is the rule +) although that value is not actually available. + In practice, such code will crash because of a value that has a wrong type, + is \begin_inset Quotes eld \end_inset -use -\begin_inset listings -inline true -status open +null +\begin_inset Quotes erd +\end_inset -\begin_layout Plain Layout +, or is a pointer to an invalid memory location. + Those errors cannot happen in a programming language whose logic of types + is consistent and whose compiler checks all types at compile time. + +\end_layout -Either +\begin_layout Standard +So, the CH correspondence gives a mathematically justified procedure for + designing new programming languages. + The procedure has the following steps: \end_layout -\end_inset +\begin_layout Itemize +Choose a formal logic that is complete and free of inconsistencies. +\end_layout +\begin_layout Itemize +For each logical operation, provide a type construction in the language. +\end_layout -\begin_inset Quotes erd -\end_inset +\begin_layout Itemize +For each axiom and proof rule of the logic, provide a code construction + in the language. +\end_layout - that matches any goal of the sequent as the proposition -\begin_inset Formula $\gamma$ +\begin_layout Standard +Mathematicians have studied different logics, such as modal logic, temporal + logic, or linear logic. + Compared with the constructive logic, those other logics have some additional + type operations. + For instance, modal logic adds the operations +\begin_inset Quotes eld \end_inset -. - But we are then required to choose two new propositions ( -\begin_inset Formula $\alpha$ +necessarily +\begin_inset Quotes erd \end_inset and -\begin_inset Formula $\beta$ +\begin_inset Quotes eld \end_inset -) such that we can prove -\begin_inset Formula $A,B\vee C\vdash\alpha\vee\beta$ +possibly +\begin_inset Quotes erd \end_inset - as well as -\begin_inset Formula $A,B\vee C,\alpha\vdash(A\wedge B)\vee C$ +, and temporal logic adds the operation +\begin_inset Quotes eld \end_inset - and -\begin_inset Formula $A,B\vee C,\beta\vdash(A\wedge B)\vee C$ +until +\begin_inset Quotes erd \end_inset . - It is not clear how we should choose -\begin_inset Formula $\alpha$ -\end_inset - - and -\begin_inset Formula $\beta$ -\end_inset - - in order to make progress in the proof. - The remaining rule, + For each logic, mathematicians have determined the minimal complete sets + of operations, axioms, and proof rules that do not lead to inconsistency. + Programming language designers can use this mathematical knowledge by choosing + a logic and translating it into a minimal \begin_inset Quotes eld \end_inset -use function +core \begin_inset Quotes erd \end_inset -, similarly requires us to choose a new proposition -\begin_inset Formula $\alpha$ -\end_inset + of a programming language. + Code in that language will be guaranteed +\emph on +never to crash +\emph default + as long as all types match. + This mathematical guarantee (known as +\begin_inset Index idx +status open + +\begin_layout Plain Layout +type safety +\end_layout - such that we can prove -\begin_inset Formula $A,B\vee C\vdash\alpha$ \end_inset - and -\begin_inset Formula $A,B\vee C\vdash\alpha\Rightarrow((A\wedge B)\vee C)$ -\end_inset -. - The rules give us no guidance for choosing -\begin_inset Formula $\alpha$ -\end_inset +\series bold +type safety +\series default +) is a powerful help for programmers since it automatically prevents a large + number of coding errors. + So, programmers will benefit if they use languages designed using the CH + correspondence. +\end_layout + +\begin_layout Standard +Practically useful programming languages will of course need more features + than the minimal set of mathematically necessary features derived from + a chosen logic. + Language designers need to make sure that all added features are consistent + with the core language. + +\end_layout + +\begin_layout Standard +At present, it is still not fully understood how a practical programming + language could use, say, modal or linear logic as its logic of types. + Experience suggests that, at least, the operations of the plain constructive + logic should be available. + So, it appears that the six type constructions and the eight code constructions + will remain available in all future languages of functional programming. + +\end_layout - appropriately. +\begin_layout Standard +It is possible to apply the FP paradigm while writing code in any programming + language. + However, some languages lack certain features that make FP techniques easier + to use in practice. + For example, in a language such as JavaScript, Python, or Ruby, one can + productively use the map/reduce operations but not disjunctive types. + More advanced FP constructions (such as typeclasses) are impractical in + these languages because the required code becomes too hard to read and + to write without errors, which negates the advantages of rigorous reasoning + about functional programs. \end_layout \begin_layout Standard -To see that the rules in Table -\begin_inset space ~ -\end_inset +Some programming languages, such as Haskell and OCaml, were designed specificall +y for advanced use in the FP paradigm. + Other languages, such as F#, Scala, Swift, PureScript, Elm, and Rust, have + different design goals but still support enough FP features to be considered + FP languages. + This book uses Scala, but the same constructions may be implemented in + other FP languages in a similar way. + At the level of detail needed in this book, the differences between languages + such as OCaml, Haskell, F#, Scala, Swift, PureScript, Elm, and Rust, do + not play a significant role. +\end_layout +\begin_layout Subsection +Practical uses of the void type (Scala's +\family typewriter +Nothing +\family default +) +\end_layout -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Proof-rules-for-constructive-logic" -plural "false" -caps "false" -noprefix "false" +\begin_layout Standard +The +\begin_inset Index idx +status open + +\begin_layout Plain Layout +void type +\end_layout \end_inset - are not helpful for proof search, note that the rules +void type +\begin_inset Foot +status open + +\begin_layout Plain Layout +The \begin_inset Quotes eld \end_inset -use function +void \begin_inset Quotes erd \end_inset - and -\begin_inset Quotes eld -\end_inset - -use + type as defined in this book is a type with no values. + It is +\emph on +not +\emph default + the same as the \begin_inset listings inline true status open \begin_layout Plain Layout -Either +void \end_layout \end_inset - -\begin_inset Quotes erd -\end_inset - - require us to choose new unknown propositions and to prove more complicated - sequents than the ones we had before. - For instance, the rule + keyword in Java or C that denotes functions returning \begin_inset Quotes eld \end_inset -use function +no value \begin_inset Quotes erd \end_inset - gives a proof of -\begin_inset Formula $\Gamma\vdash\beta$ -\end_inset - - only if we first choose some other proposition -\begin_inset Formula $\alpha$ -\end_inset +. + Those functions are equivalent to Scala functions returning the +\begin_inset listings +inline true +status open - and prove the sequents -\begin_inset Formula $\Gamma\vdash\alpha$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $\Gamma\vdash\alpha\Rightarrow\beta$ -\end_inset +Unit +\end_layout -. - The rule does not say how to choose the proposition -\begin_inset Formula $\alpha$ \end_inset - correctly. - We need to guess the correct -\begin_inset Formula $\alpha$ -\end_inset + type. +\end_layout - by trial and error. - Even after choosing -\begin_inset Formula $\alpha$ \end_inset - in some way, we will have to prove a more complicated sequent ( -\begin_inset Formula $\Gamma\vdash\alpha\Rightarrow\beta$ -\end_inset + (Scala's +\begin_inset listings +inline true +status open -). - It is not guaranteed that we are getting closer to finding the proof of - the initial sequent ( -\begin_inset Formula $\Gamma\vdash\beta$ -\end_inset +\begin_layout Plain Layout -). - +Nothing \end_layout -\begin_layout Standard -It is far from obvious how to overcome that difficulty. - Mathematicians have studied the constructive logic for more than 60 years, - trying to replace the rules in Table -\begin_inset space ~ \end_inset - -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Proof-rules-of-constructive-and-boolean" -plural "false" -caps "false" -noprefix "false" - +) corresponds to the logical constant +\begin_inset Formula $False$ \end_inset - by a different but equivalent set of derivation rules that require no guessing - when looking for a proof. - The first partial success came in 1935 with an algorithm called +. + (The proposition \begin_inset Quotes eld \end_inset -LJ + +\emph on +the code can compute a value of the void type +\emph default + \begin_inset Quotes erd \end_inset -. -\begin_inset Foot + is always false.) The void type is used in some theoretical proofs but has + few practical uses. + One use case is for a branch of a +\begin_inset listings +inline true status open \begin_layout Plain Layout -See -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://en.wikipedia.org/wiki/Sequent_calculus#Overview" -literal "false" +match +\end_layout \end_inset +/ +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +case \end_layout \end_inset - The LJ algorithm still has a significant problem: one of its derivation - rules may be applied infinitely many times. - So, the LJ algorithm is not guaranteed to terminate without some heuristics - for avoiding an infinite loop. - A terminating version of the LJ algorithm, called + expression that throws an \begin_inset Index idx status open \begin_layout Plain Layout -LJT algorithm +exception \end_layout \end_inset -LJT, was formulated in 1992. -\begin_inset Foot +exception instead of returning a value. + In this sense, returning a value of the void type corresponds to a crash + in the program. + So, a +\begin_inset listings +inline true status open \begin_layout Plain Layout -The history of that research is described in -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://research-repository.st-andrews.ac.uk/handle/10023/8824" -literal "false" +throw +\end_layout \end_inset + expression is defined as if it returns a value of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Nothing +\end_layout + +\end_inset -\family default . - An often cited paper by R. + We can then pretend to convert that +\begin_inset Quotes eld +\end_inset + +value +\begin_inset Quotes erd +\end_inset + + (which will never be actually returned) into a value of any other type. + Example \begin_inset space ~ \end_inset -Dyckhoff -\begin_inset Index idx + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Example-type-identity-0-to-A" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + shows how to write a function +\begin_inset listings +inline true status open \begin_layout Plain Layout -Roy Dyckhoff + +absurd[A] \end_layout \end_inset - is found at -\family typewriter - -\begin_inset CommandInset href -LatexCommand href -target "https://philpapers.org/rec/DYCCSC" -literal "false" - -\end_inset + of type +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +Nothing => A \end_layout \end_inset - +. \end_layout \begin_layout Standard -We will first present the LJ algorithm. - Although that algorithm does not guarantee termination, it is simpler to - understand and to apply by hand. - Then we will show how to modify the LJ algorithm in order to obtain the - always-terminating LJT algorithm. -\end_layout +To see how this trick is used, consider this code defining a value +\begin_inset listings +inline true +status open -\begin_layout Subsubsection* -The LJ algorithm +\begin_layout Plain Layout + +x \end_layout -\begin_layout Standard -Figure -\begin_inset space ~ \end_inset +: +\begin_inset listings +inline false +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:Rules-of-the-LJ-algorithm" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout +val x: Double = if (t >= 0.0) math.sqrt(t) else throw new Exception( +\begin_inset Quotes eld \end_inset - shows the LJ algorithm's axioms and derivation rules. - Each rule says that the bottom sequent will be proved if proofs are given - for sequent(s) at the top. - For each possible sub-expression (conjunction -\begin_inset Formula $X\wedge Y$ +error +\begin_inset Quotes erd \end_inset -, disjunction -\begin_inset Formula $X\vee Y$ -\end_inset +) +\end_layout -, and implication -\begin_inset Formula $X\Rightarrow Y$ \end_inset -) there is one rule where that sub-expression is a premise (at -\begin_inset Quotes eld -\end_inset +The +\begin_inset listings +inline true +status open -left -\begin_inset Quotes erd -\end_inset +\begin_layout Plain Layout -) and one rule where that sub-expression is the goal (at -\begin_inset Quotes eld -\end_inset +else +\end_layout -right -\begin_inset Quotes erd \end_inset -). - Those sub-expressions are shown in blue in Figure -\begin_inset space ~ -\end_inset + branch does not return a value, but +\begin_inset listings +inline true +status open +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:Rules-of-the-LJ-algorithm" -plural "false" -caps "false" -noprefix "false" +x +\end_layout \end_inset - to help us look for a proof. - To find out which rules apply, we match some part of the sequent with a - blue sub-expression. -\end_layout - -\begin_layout Standard -\begin_inset Float figure -wide false -sideways false + is declared to have type +\begin_inset listings +inline true status open \begin_layout Plain Layout -\align center -\begin_inset Box Boxed -position "t" -hor_pos "c" -has_inner_box 1 -inner_pos "t" -use_parbox 0 -use_makebox 0 -width "70line%" -special "none" -height "1in" -height_special "totalheight" -thickness "0.4pt" -separation "3pt" -shadowsize "4pt" -framecolor "black" -backgroundcolor "none" -status open -\begin_layout Plain Layout -\begin_inset Formula -\begin{align*} -\frac{}{\Gamma,X\vdash{\color{blue}X}}~(\text{Id})\qquad & \qquad\frac{}{\Gamma\vdash{\color{blue}\top}}~(\text{True})\\ -\frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\Rightarrow B}\vdash C}~(\text{Left}\Rightarrow)\qquad & \qquad\frac{\Gamma,A\vdash B}{\Gamma\vdash{\color{blue}A\Rightarrow B}}~(\text{Right}\Rightarrow)\\ -\frac{\Gamma,A_{i}\vdash C}{\Gamma,{\color{blue}A_{1}\wedge A_{2}}\vdash C}~(\text{Left}\wedge_{i})\qquad & \qquad\frac{\Gamma\vdash A\quad\quad\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\wedge B}}~(\text{Right}\wedge)\\ -\frac{\Gamma,A\vdash C\quad\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\vee B}\vdash C}~(\text{Left}\vee)\qquad & \qquad\frac{\Gamma\vdash A_{i}}{\Gamma\vdash{\color{blue}A_{1}\vee A_{2}}}~(\text{Right}\vee_{i}) -\end{align*} +Double +\end_layout \end_inset +. + For this code to type-check, both branches must return values of the same + type. + So, the compiler needs to pretend that the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +else \end_layout \end_inset - -\end_layout + branch also returns a value of type +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\begin_inset Caption Standard -\begin_layout Plain Layout -Axioms and derivation rules of the LJ algorithm. - Each of the rules -\begin_inset Quotes eld -\end_inset +Double +\end_layout -( -\begin_inset Formula $\text{Left}\wedge_{i}$ \end_inset -) -\begin_inset Quotes erd -\end_inset +. + The compiler first assigns the type +\begin_inset listings +inline true +status open - and -\begin_inset Quotes eld -\end_inset +\begin_layout Plain Layout -( -\begin_inset Formula $\text{Right}\vee_{i}$ -\end_inset +Nothing +\end_layout -) -\begin_inset Quotes erd \end_inset - have two versions, with -\begin_inset Formula $i=1$ -\end_inset + to the expression +\begin_inset listings +inline true +status open - or -\begin_inset Formula $i=2$ -\end_inset +\begin_layout Plain Layout -. - -\begin_inset CommandInset label -LatexCommand label -name "fig:Rules-of-the-LJ-algorithm" +throw ... +\end_layout \end_inset + and then automatically uses the conversion +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +Nothing => Double \end_layout \end_inset + to convert that type to +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +Double \end_layout \end_inset +. + In this way, types will match in the definition of the value +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +x \end_layout -\begin_layout Standard -It turns out that the rules in Figure -\begin_inset space ~ \end_inset +. + +\end_layout -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:Rules-of-the-LJ-algorithm" -plural "false" -caps "false" -noprefix "false" - -\end_inset +\begin_layout Standard +This book does not discuss exceptions in much detail. + The functional programming paradigm does not use exceptions because their + presence significantly complicates reasoning about code. +\end_layout - are -\emph on -equivalent -\emph default - to the rules in Table -\begin_inset space ~ -\end_inset +\begin_layout Standard +As another example of using the void type, suppose an external library implement +s a function: +\begin_inset listings +inline false +status open +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "tab:Proof-rules-for-constructive-logic" -plural "false" -caps "false" -noprefix "false" +def parallel_run[E, A, B](f: A => Either[E, B]): Either[E, B] = ??? +\end_layout \end_inset -. - The proof is beyond the scope of this book. - We only remark that this equivalence is far from obvious. - To prove it, one needs to demonstrate that any sequent derived through - the first set of rules is also derivable through the second set, and vice - versa. -\end_layout +We may imagine that +\begin_inset listings +inline true +status open -\begin_layout Standard -To illustrate the LJ algorithm, let us prove the sequent -\begin_inset space ~ -\end_inset +\begin_layout Plain Layout -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:ch-example-sequent-2" -plural "false" -caps "false" -noprefix "false" +parallel_run(f) +\end_layout \end_inset -). - Denote that sequent by -\begin_inset Formula $S_{0}$ + performs some parallel computations using a given function +\begin_inset Formula $f$ \end_inset -: -\begin_inset Formula -\[ -S_{0}\triangleq\emptyset\vdash\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\Rightarrow\beta\quad. -\] - +. + In general, functions +\begin_inset Formula $f^{:A\rightarrow E+B}$ \end_inset - Since the goal of -\begin_inset Formula $S_{0}$ + may return an error of type +\begin_inset Formula $E$ \end_inset - contains an implication, we use the rule -\begin_inset Quotes eld + or a result of type +\begin_inset Formula $B$ \end_inset -( -\begin_inset Formula $\text{Right}\Rightarrow$ +. + Suppose we know that a particular function +\begin_inset Formula $f$ \end_inset -) -\begin_inset Quotes erd + never fails to compute its result. + To express that knowledge in code, we may explicitly set the type parameter + +\begin_inset Formula $E$ \end_inset - and get a sequent -\begin_inset Formula $S_{1}$ -\end_inset + to the void type +\begin_inset listings +inline true +status open -: -\begin_inset Formula -\[ -S_{1}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\beta\quad. -\] +\begin_layout Plain Layout -\end_inset +Nothing +\end_layout -Now the implication is in the premise, so we use the rule -\begin_inset Quotes eld \end_inset -( -\begin_inset Formula $\text{Left}\Rightarrow$ -\end_inset + when applying +\begin_inset listings +inline true +status open -) -\begin_inset Quotes erd -\end_inset +\begin_layout Plain Layout - and get two new sequents: -\begin_inset Formula -\[ -S_{2}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\alpha\Rightarrow\alpha\quad,\quad\quad S_{3}\triangleq\beta\vdash\beta\quad. -\] +parallel_run +\end_layout \end_inset -Sequent -\begin_inset Formula $S_{3}$ -\end_inset +: +\begin_inset listings +inline false +status open - follows from the -\begin_inset Quotes eld -\end_inset +\begin_layout Plain Layout -(Id) -\begin_inset Quotes erd -\end_inset +parallel_run[Nothing, A, B](f) // Types match only when values f(a) always + are of the form Right(b). + +\end_layout - axiom, so it remains to prove -\begin_inset Formula $S_{2}$ \end_inset -. - Since -\begin_inset Formula $S_{2}$ -\end_inset +Returning an error is now impossible (the type +\begin_inset listings +inline true +status open - contains an implication both as a premise and as the goal, we may apply - either the rule -\begin_inset Quotes eld -\end_inset +\begin_layout Plain Layout -( -\begin_inset Formula $\text{Left}\Rightarrow$ -\end_inset +Nothing +\end_layout -) -\begin_inset Quotes erd \end_inset - or the rule -\begin_inset Quotes eld + has no values). + If the function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +parallel_run +\end_layout + \end_inset -( -\begin_inset Formula $\text{Right}\Rightarrow$ + is fully parametric, it will work in the same way with all types +\begin_inset Formula $E$ \end_inset -) -\begin_inset Quotes erd +, including +\begin_inset Formula $E=\bbnum 0$ \end_inset . - We choose to apply -\begin_inset Quotes eld -\end_inset + The code implements our intention via type parameters, giving a compile-time + guarantee of correct results. +\end_layout -( -\begin_inset Formula $\text{Left}\Rightarrow$ -\end_inset +\begin_layout Standard +So far, none of our examples involved the logical +\series bold +negation +\series default + +\begin_inset Index idx +status open + +\begin_layout Plain Layout +negation (in logic) +\end_layout -) -\begin_inset Quotes erd \end_inset - and get two new sequents: + operation. + It is defined as: \begin_inset Formula \[ -S_{4}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\alpha\Rightarrow\alpha\quad,\quad\quad S_{5}:\beta\vdash\alpha\Rightarrow\alpha\quad. +\neg\alpha\triangleq(\alpha\Rightarrow False)\quad. \] \end_inset -Notice that -\begin_inset Formula $S_{4}=S_{2}$ +Its practical use is as limited as that of +\begin_inset Formula $False$ \end_inset -. - So, our proof search is getting into an infinite loop trying to prove the - same sequent -\begin_inset Formula $S_{2}$ -\end_inset + and the void type. + However, logical negation plays an important role in Boolean logic, which + we will discuss next. +\end_layout + +\begin_layout Subsection +Relationship between Boolean logic and constructive logic +\begin_inset CommandInset label +LatexCommand label +name "subsec:Relationship-between-Boolean" - over and over again. - We can prove -\begin_inset Formula $S_{5}$ \end_inset - but this will not help us break the loop. + \end_layout \begin_layout Standard -Once we recognize the problem, we backtrack to the point where we chose - to apply -\begin_inset Quotes eld +We have seen that some true theorems of Boolean logic are not true in constructi +ve logic. + For example, the Boolean identities +\begin_inset Formula $\neg\left(\neg\alpha\right)=\alpha$ \end_inset -( -\begin_inset Formula $\text{Left}\Rightarrow$ + and +\begin_inset Formula $\left(\alpha\Rightarrow\beta\right)=(\neg\alpha\vee\beta)$ \end_inset -) -\begin_inset Quotes erd + do not hold in the constructive logic. + However, as we will now show, any theorem of constructive logic is also + a theorem of Boolean logic. + The reason is that all eight rules of constructive logic (Section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:The-rules-of-proof" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +) are also true in Boolean logic. +\end_layout + +\begin_layout Standard +To verify that a formula is true in Boolean logic, it is sufficient to + check that the value of the formula is +\begin_inset Formula $True$ \end_inset - to -\begin_inset Formula $S_{2}$ + for all possible truth values ( +\begin_inset Formula $True$ \end_inset -. - That was a bad choice, so let us instead apply -\begin_inset Quotes eld + or +\begin_inset Formula $False$ \end_inset -( -\begin_inset Formula $\text{Right}\Rightarrow$ +) of its variables. + A sequent such as +\begin_inset Formula $\alpha,\beta\vdash\gamma$ \end_inset -) -\begin_inset Quotes erd + is true in Boolean logic if and only if +\begin_inset Formula $\gamma=True$ \end_inset - to -\begin_inset Formula $S_{2}$ + under the assumption that +\begin_inset Formula $\alpha=\beta=True$ \end_inset . - This yields a new sequent -\begin_inset Formula $S_{6}$ + So, the sequent +\begin_inset Formula $\alpha,\beta\vdash\gamma$ \end_inset -: + is translated into the Boolean formula: \begin_inset Formula \[ -S_{6}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta,\alpha\vdash\alpha\quad. +\alpha,\beta\vdash\gamma=\left(\left(\alpha\wedge\beta\right)\Rightarrow\gamma\right)=\left(\neg\alpha\vee\neg\beta\vee\gamma\right)\quad. \] \end_inset -This sequent follows from the -\begin_inset Quotes eld +Table +\begin_inset space ~ \end_inset -(Id) -\begin_inset Quotes erd + +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Proof-rules-of-constructive-and-boolean" +plural "false" +caps "false" +noprefix "false" + \end_inset -axiom. - There are no more sequents to prove, so the proof of -\begin_inset Formula $S_{0}$ + translates all proof rules of Section +\begin_inset space ~ \end_inset - is finished. - It can be drawn as a -\begin_inset Index idx -status open -\begin_layout Plain Layout -proof tree -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:The-rules-of-proof" +plural "false" +caps "false" +noprefix "false" \end_inset + into Boolean formulas. + The first two lines are axioms, while the subsequent lines are Boolean + theorems that can be verified by calculation. +\end_layout + +\begin_layout Standard +\begin_inset Float table +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Tabular + + + + + + +\begin_inset Text + +\begin_layout Plain Layout \series bold -proof tree -\series default - like this: -\begin_inset Formula -\[ -\xymatrix{\xyScaleY{1.5pc}\xyScaleX{4pc} & & & (\text{Id})\\ -\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[r]\sp(0.5){S_{2}}\ar[ru]\sp(0.6){S_{3}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){S_{6}} & (\text{Id}) -} -\] +\size small +Constructive logic +\end_layout \end_inset + + +\begin_inset Text -The nodes of the proof tree are axioms or derivation rules, and the edges - are intermediate sequents required by the rules. - Some rule nodes branch into several sequents because some rules require - more than one new sequent to be proved. - The leaves of the tree are axioms that do not require proving any further - sequents. - -\end_layout +\begin_layout Plain Layout -\begin_layout Subsubsection* -Extracting code from proofs +\series bold +\size small +Boolean logic \end_layout -\begin_layout Standard -According to the Curry-Howard correspondence, a sequent (such as -\begin_inset Formula $A,B,...,C\vdash X$ \end_inset + + + + +\begin_inset Text -) represents the task of writing a fully parametric code expression of type - -\begin_inset Formula $X$ -\end_inset +\begin_layout Plain Layout - that uses some given values of types -\begin_inset Formula $A$ +\size small +\begin_inset Formula $\frac{}{\Gamma\vdash{\cal CH}(\bbnum 1)}\quad(\text{create unit})$ \end_inset -, -\begin_inset Formula $B$ -\end_inset -, ..., -\begin_inset Formula $C$ -\end_inset +\end_layout -. - The sequent is true (i.e., can be proved) if that code expression can be - found. - So, the code serves as an -\begin_inset Quotes eld \end_inset + + +\begin_inset Text -evidence of proof -\begin_inset Quotes erd +\begin_layout Plain Layout + +\size small +\begin_inset Formula $\neg\Gamma\vee True=True$ \end_inset - for the sequent. -\end_layout -\begin_layout Standard -\begin_inset Float figure -wide false -sideways false -status open +\end_layout -\begin_layout Plain Layout -\align center -\begin_inset Box Boxed -position "t" -hor_pos "c" -has_inner_box 1 -inner_pos "t" -use_parbox 0 -use_makebox 0 -width "100col%" -special "none" -height "1in" -height_special "totalheight" -thickness "0.4pt" -separation "3pt" -shadowsize "4pt" -framecolor "black" -backgroundcolor "none" -status open +\end_inset + + + + +\begin_inset Text \begin_layout Plain Layout -\begin_inset Formula -\begin{align*} -\frac{}{\Gamma,A\vdash{\color{blue}A}}~(\text{Id})\quad & \quad\text{Proof}\,(\Gamma,A\vdash A)_{\text{given }p^{:\Gamma},x^{:A}}=x\\ -\frac{}{\Gamma\vdash{\color{blue}\top}}~(\text{True})\quad & \quad\text{Proof}\,(\Gamma\vdash\top)_{\text{given }p^{:\Gamma}}=1\\ -\frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\Rightarrow B}\vdash C}~(\text{Left}\Rightarrow)\quad & \quad\text{Proof}\,(\Gamma,A\Rightarrow B\vdash C)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p,b^{:B}}\\ - & \quad\quad\text{where}\quad b^{:B}\triangleq q\big(\text{Proof}\,(\Gamma,A\Rightarrow B\vdash A)_{\text{given }p,q}\big)\\ -\frac{\Gamma,A\vdash B}{\Gamma\vdash{\color{blue}A\Rightarrow B}}~(\text{Right}\Rightarrow)\quad & \quad\text{Proof}\,(\Gamma\vdash A\Rightarrow B)_{\text{given }p^{:\Gamma}}=x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash B)_{\text{given }p^{:\Gamma},x^{:A}}\\ -\frac{\Gamma,A\vdash C}{\Gamma,{\color{blue}A\wedge B}\vdash C}~(\text{Left}\wedge_{1})\quad & \quad\text{Proof}\,(\Gamma,A\wedge B\vdash C)_{\text{given }p^{:\Gamma},(a^{:A}\times b^{:B})}\\ - & \quad\quad=\text{Proof}\,(\Gamma,A\vdash C)_{\text{given }p^{:\Gamma},a^{:A}}\\ -\frac{\Gamma,B\vdash C}{\Gamma,{\color{blue}A\wedge B}\vdash C}~(\text{Left}\wedge_{2})\quad & \quad\text{Proof}\,(\Gamma,A\wedge B\vdash C)_{\text{given }p^{:\Gamma},(a^{:A}\times b^{:B})}\\ - & \quad\quad=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p^{:\Gamma},b^{:B}}\\ -\frac{\Gamma\vdash A\quad\quad\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\wedge B}}~(\text{Right}\wedge)\quad & \quad\text{Proof}\,(\Gamma\vdash A\wedge B)_{\text{given }p^{:\Gamma}}\\ - & \quad\quad=\text{Proof}\,(\Gamma\vdash A)_{\text{given }p^{:\Gamma}}\times\text{Proof}\,(\Gamma\vdash B)_{\text{given }p^{:\Gamma}}\\ -\frac{\Gamma,A\vdash C\quad\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\vee B}\vdash C}~(\text{Left}\vee)\quad & \quad\text{Proof}\,(\Gamma,A\vee B\vdash C)_{\text{given }p^{:\Gamma},q^{:A+B}}\\ - & \quad\quad=q\triangleright\begin{array}{|c||c|} - & C\\ -\hline A & x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash C)_{\text{given }p,x}\\ -B & y^{:B}\rightarrow\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p,y} -\end{array}\\ -\frac{\Gamma\vdash A}{\Gamma\vdash{\color{blue}A\vee B}}~(\text{Right}\vee_{1})\quad & \quad\text{Proof}\,(\Gamma\vdash A\vee B)_{\text{given }p^{:\Gamma}}=\text{Proof}\,(\Gamma\vdash A)+\bbnum 0^{:B}\\ -\frac{\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\vee B}}~(\text{Right}\vee_{2})\quad & \quad\text{Proof}\,(\Gamma\vdash A\vee B)_{\text{given }p^{:\Gamma}}=\bbnum 0^{:A}+\text{Proof}\,(\Gamma\vdash B) -\end{align*} +\size small +\begin_inset Formula $\frac{~}{\Gamma,\alpha\vdash\alpha}\quad(\text{use arg})$ \end_inset \end_layout \end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\size small +\begin_inset Formula $\neg\Gamma\vee\neg\alpha\vee\alpha=True$ +\end_inset \end_layout -\begin_layout Plain Layout -\begin_inset Caption Standard +\end_inset + + + + +\begin_inset Text \begin_layout Plain Layout -\begin_inset CommandInset label -LatexCommand label -name "fig:proof-transformers-for-LJ-rules" +\size small +\begin_inset Formula $\frac{\Gamma,\alpha\vdash\beta}{\Gamma\vdash\alpha\Rightarrow\beta}\quad(\text{create function})$ \end_inset -Proof transformers for the rules of the LJ algorithm. + \end_layout \end_inset + + +\begin_inset Text +\begin_layout Plain Layout -\end_layout - +\size small +\begin_inset Formula $\left(\neg\Gamma\vee\neg\alpha\vee\beta\right)=\left(\neg\Gamma\vee\left(\alpha\Rightarrow\beta\right)\right)$ \end_inset \end_layout -\begin_layout Standard -In the previous subsection, we have found a proof of the sequent -\begin_inset Formula $S_{0}$ \end_inset + + + + +\begin_inset Text -, which represents the task of writing a fully parametric function with - type signature -\begin_inset Formula $(\left(A\rightarrow A\right)\rightarrow B)\rightarrow B$ -\end_inset +\begin_layout Plain Layout -). - Let us now see how we can extract the code of that function from the proof - of the sequent -\begin_inset Formula $S_{0}$ +\size small +\begin_inset Formula $\frac{\Gamma\vdash\alpha\quad\Gamma\vdash\alpha\Rightarrow\beta}{\Gamma\vdash\beta}\quad(\text{use function})$ \end_inset -. + \end_layout -\begin_layout Standard -We start from the leaves of the proof tree and move step by step towards - the initial sequent. - At each step, we shorten the proof tree by replacing some sequent by its - corresponding evidence-of-proof code. - Eventually we will replace the initial sequent by its corresponding code. - Let us see how this procedure works for the proof tree of the sequent -\begin_inset Formula $S_{0}$ \end_inset + + +\begin_inset Text - shown in the previous section. -\end_layout - -\begin_layout Standard -Since the leaves are axioms, let us write the code corresponding to each - axiom of LJ: -\begin_inset Formula -\begin{align*} - & \frac{}{\Gamma,X\vdash X}~(\text{Id})\quad:\quad\quad\text{Proof}\,(\Gamma,X\vdash X)_{\text{given }p^{:\Gamma},x^{:X}}=x\quad;\\ - & \frac{}{\Gamma\vdash\top}~(\text{True})\quad:\quad\quad\text{Proof}\,(\Gamma\vdash\top)_{\text{given }p^{:\Gamma}}=1\quad. -\end{align*} +\begin_layout Plain Layout +\size small +\begin_inset Formula $\left(\left(\neg\Gamma\vee\alpha\right)\wedge\left(\neg\Gamma\vee\left(\alpha\Rightarrow\beta\right)\right)\right)\Rightarrow\left(\neg\Gamma\vee\beta\right)$ \end_inset -Here we denote explicitly the values (such as -\begin_inset Formula $p$ -\end_inset - and -\begin_inset Formula $x$ -\end_inset +\end_layout -) given as premises to the sequent. - The notation -\begin_inset Formula $p^{:\Gamma}$ \end_inset + + + + +\begin_inset Text - means all values given in the set of premises -\begin_inset Formula $\Gamma$ -\end_inset +\begin_layout Plain Layout -. - Below we will assume that the propositions -\begin_inset Formula $\alpha$ +\size small +\begin_inset Formula $\frac{\Gamma\vdash\alpha\quad\Gamma\vdash\beta}{\Gamma\vdash\alpha\wedge\beta}\quad(\text{create tuple})$ \end_inset - and -\begin_inset Formula $\beta$ -\end_inset - correspond to types -\begin_inset Formula $A$ -\end_inset +\end_layout - and -\begin_inset Formula $B$ \end_inset + + +\begin_inset Text -; that is, -\begin_inset Formula $\alpha\triangleq{\cal CH}(A)$ -\end_inset +\begin_layout Plain Layout - and -\begin_inset Formula $\beta\triangleq{\cal CH}(B)$ +\size small +\begin_inset Formula $\left(\neg\Gamma\vee\alpha\right)\wedge\left(\neg\Gamma\vee\beta\right)=\left(\neg\Gamma\vee\left(\alpha\wedge\beta\right)\right)$ \end_inset -. + \end_layout -\begin_layout Standard -The leaves in the proof tree for -\begin_inset Formula $S_{0}$ \end_inset + + + + +\begin_inset Text - are the -\begin_inset Quotes eld -\end_inset +\begin_layout Plain Layout -( -\begin_inset Formula $\text{Id}$ +\size small +\begin_inset Formula $\frac{\Gamma\vdash\alpha\wedge\beta}{\Gamma\vdash\alpha}\quad(\text{use tuple-}1)$ \end_inset -) -\begin_inset Quotes erd -\end_inset - axioms used to prove the sequents -\begin_inset Formula $S_{3}$ -\end_inset +\end_layout - and -\begin_inset Formula $S_{6}$ \end_inset + + +\begin_inset Text -. - Let us write the code that serves as the -\begin_inset Quotes eld -\end_inset +\begin_layout Plain Layout -evidence of proof -\begin_inset Quotes erd +\size small +\begin_inset Formula $\left(\neg\Gamma\vee\left(\alpha\wedge\beta\right)\right)\Rightarrow\left(\neg\Gamma\vee\alpha\right)$ \end_inset - for these sequents. - For brevity, we denote -\begin_inset Formula $\gamma\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta$ -\end_inset - and -\begin_inset Formula $C\triangleq\left(A\rightarrow A\right)\rightarrow B$ -\end_inset +\end_layout -, so that -\begin_inset Formula $\gamma={\cal CH}(C)$ \end_inset + + + + +\begin_inset Text -. - Then we can write: -\begin_inset Formula -\begin{align*} - & S_{3}\triangleq\beta\vdash\beta\quad,\quad\quad\text{Proof}\,(S_{3})_{\text{given }y^{:B}}=y\quad,\\ - & S_{6}\triangleq\gamma,\alpha\vdash\alpha\quad,\quad\quad\text{Proof}\,(S_{6})_{\text{given }q^{:C},x^{:A}}=x\quad. -\end{align*} +\begin_layout Plain Layout +\size small +\begin_inset Formula $\frac{\Gamma\vdash\alpha}{\Gamma\vdash\alpha\vee\beta}\quad(\text{create Left})$ \end_inset -Note that the proof of -\begin_inset Formula $S_{6}$ -\end_inset - does not use the first given value -\begin_inset Formula $q^{:C}$ +\end_layout + \end_inset + + +\begin_inset Text - (corresponding to the premise -\begin_inset Formula $\gamma$ +\begin_layout Plain Layout + +\size small +\begin_inset Formula $\left(\neg\Gamma\vee\alpha\right)\Rightarrow\left(\neg\Gamma\vee\left(\alpha\vee\beta\right)\right)$ \end_inset -). + \end_layout -\begin_layout Standard -We now shorten the proof tree by replacing the sequents -\begin_inset Formula $S_{3}$ \end_inset + + + + +\begin_inset Text - and -\begin_inset Formula $S_{6}$ -\end_inset +\begin_layout Plain Layout - by their -\begin_inset Quotes eld +\size small +\begin_inset Formula $\frac{\Gamma\vdash\alpha\vee\beta\quad\Gamma,\alpha\vdash\gamma\quad\Gamma,\beta\vdash\gamma}{\Gamma\vdash\gamma}\quad(\text{use Either})$ \end_inset -evidence of proof -\begin_inset Quotes erd + +\end_layout + \end_inset + + +\begin_inset Text -: -\begin_inset Formula -\[ -\xymatrix{\xyScaleY{1.5pc}\xyScaleX{4pc} & & & \square\\ -\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[r]\sp(0.5){S_{2}}\ar[ru]\sp(0.6){(y)_{\text{given }y^{:B}}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){(x)_{\text{given }q^{:C},x^{:A}}} & \square -} -\] +\begin_layout Plain Layout +\size small +\begin_inset Formula $\left(\neg\Gamma\vee\alpha\vee\beta\right)\wedge\left(\neg\Gamma\vee\neg\alpha\vee\gamma\right)\quad\quad$ \end_inset \end_layout -\begin_layout Standard -The next step is to consider the proof of -\begin_inset Formula $S_{2}$ \end_inset + + + + +\begin_inset Text -, which is found by applying the rule -\begin_inset Quotes eld -\end_inset +\begin_layout Plain Layout -( -\begin_inset Formula $\text{Right}\Rightarrow$ -\end_inset +\end_layout -) -\begin_inset Quotes erd \end_inset + + +\begin_inset Text -. - This rule promises to give a proof of -\begin_inset Formula $S_{2}$ +\begin_layout Plain Layout +\begin_inset Formula $\quad\quad\wedge\left(\neg\Gamma\vee\neg\beta\vee\gamma\right)\Rightarrow\left(\neg\Gamma\vee\gamma\right)$ \end_inset - if we have a proof of -\begin_inset Formula $S_{6}$ -\end_inset -. - In order to extract code from that rule, we can write a function that transform -s a proof of -\begin_inset Formula $S_{6}$ +\end_layout + \end_inset + + + - into a proof of -\begin_inset Formula $S_{2}$ \end_inset -. - We call this function the -\series bold -proof transformer -\series default -\begin_inset Index idx -status open +\end_layout \begin_layout Plain Layout -Curry-Howard correspondence!proof transformer -\end_layout +\begin_inset Caption Standard -\end_inset +\begin_layout Plain Layout +Proof rules of constructive logic are true also in the Boolean logic. +\begin_inset CommandInset label +LatexCommand label +name "tab:Proof-rules-of-constructive-and-boolean" +\end_inset -\begin_inset Index idx -status open -\begin_layout Plain Layout -proof transformer \end_layout \end_inset - corresponding to the rule -\begin_inset Quotes eld + +\end_layout + \end_inset -( -\begin_inset Formula $\text{Right}\Rightarrow$ + +\end_layout + +\begin_layout Standard +To simplify the calculations, note that all terms in the formulas contain + the operation +\begin_inset Formula $\left(\neg\Gamma\vee...\right)$ \end_inset -) -\begin_inset Quotes erd + corresponding to the context +\begin_inset Formula $\Gamma$ \end_inset . - That rule and its transformer are defined as: -\begin_inset Formula -\[ -\frac{\Gamma,A\vdash B}{\Gamma\vdash A\Rightarrow B}~(\text{Right}\Rightarrow)\quad:\quad\quad\text{Proof}\,(\Gamma\vdash A\Rightarrow B)_{\text{given }p^{:\Gamma}}=x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash B)_{\text{given }p^{:\Gamma},x^{:A}}\quad. -\] + Now, if +\begin_inset Formula $\Gamma$ +\end_inset + is +\begin_inset Formula $False$ \end_inset -Applying the proof transformer to the known proof of -\begin_inset Formula $S_{6}$ +, the entire formula becomes automatically +\begin_inset Formula $True$ \end_inset -, we obtain a proof of -\begin_inset Formula $S_{2}$ +, and there is nothing else to check. + So, it remains to verify the formula in case +\begin_inset Formula $\Gamma=True$ \end_inset -: -\begin_inset Formula -\[ -\text{Proof}\,(S_{2})_{\text{given }q^{:C}}=x^{:A}\rightarrow\text{Proof}\,(S_{6})_{\text{given }q^{:C},x^{:A}}=(x^{:A}\rightarrow x)_{\text{given }q^{:C}}\quad. -\] +, and then we can simply omit all instances of +\begin_inset Formula $\neg\Gamma$ +\end_inset + in the formulas. + Let us show the Boolean derivations for the rules +\begin_inset Quotes eld \end_inset -The proof tree can be now shortened to: -\begin_inset Formula -\[ -\xymatrix{\xyScaleY{1.5pc}\xyScaleX{3.5pc} & & & \square\\ -\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[rr]\sp(0.62){(x^{:A}\rightarrow x)_{\text{given }q^{:C}}}\ar[ru]\sp(0.6){(y)_{\text{given }y^{:B}}} & & \square -} -\] +\begin_inset Formula $\text{use function}$ \end_inset -\end_layout - -\begin_layout Standard -The next step is to get the proof of -\begin_inset Formula $S_{1}$ +\begin_inset Quotes erd \end_inset - obtained by applying the rule + and \begin_inset Quotes eld \end_inset -( -\begin_inset Formula $\text{Left}\Rightarrow$ + +\begin_inset Formula $\text{use Either}$ \end_inset -) + \begin_inset Quotes erd \end_inset -. - That rule requires two previous sequents, so its transformer is a function - of two previously obtained proofs: +; other formulas are checked in a similar way: \begin_inset Formula \begin{align*} - & \frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,A\Rightarrow B\vdash C}~(\text{Left}\Rightarrow)\quad:\\ - & \text{Proof}\,(\Gamma,A\Rightarrow B\vdash C)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p^{:\Gamma},b^{:B}}\\ - & \quad\quad\text{where}\quad b^{:B}\triangleq q\big(\text{Proof}\,(\Gamma,A\Rightarrow B\vdash A)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}\big)\quad. +\text{formula ``use function''}:\quad & \left(\alpha\wedge\left(\alpha\Rightarrow\beta\right)\right)\Rightarrow\beta\\ +\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & =\gunderline{\neg}(\alpha\,\gunderline{\wedge}\,(\neg\alpha\,\gunderline{\vee}\,\beta))\vee\beta\\ +\text{de Morgan's laws}:\quad & =\gunderline{\neg\alpha\vee(\alpha\wedge\neg\beta)}\vee\beta \end{align*} \end_inset -In the proof tree shown above, we obtain a proof of -\begin_inset Formula $S_{1}$ -\end_inset - by applying this proof transformer to the proofs of -\begin_inset Formula $S_{2}$ -\end_inset +\begin_inset Formula +\begin{align*} +\text{identity }p\vee(\neg p\wedge q)=p\vee q\text{ with }p=\neg\alpha\text{ and }q=\beta:\quad & =\neg\alpha\vee\gunderline{\neg\beta\vee\beta}\\ +\text{axiom ``use arg''}:\quad & =True\quad. +\end{align*} - and -\begin_inset Formula $S_{3}$ \end_inset -: + \begin_inset Formula \begin{align*} - & \text{Proof}\,(S_{1})_{\text{given }q^{:C}}=\text{Proof}\,(S_{3})_{\text{given }b^{:B}}\text{ where }b^{:B}\triangleq q(\text{Proof}\,(S_{2}))_{\text{given }q^{:C}}\\ - & \quad=b\text{ where }b^{:B}\triangleq q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}=q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}\quad. +\text{formula ``use Either''}:\quad & \left(\left(\alpha\vee\beta\right)\wedge\left(\alpha\Rightarrow\gamma\right)\wedge\left(\beta\Rightarrow\gamma\right)\right)\Rightarrow\gamma\\ +\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:\quad & =\neg\left(\left(\alpha\vee\beta\right)\wedge\left(\neg\alpha\vee\gamma\right)\wedge\left(\neg\beta\vee\gamma\right)\right)\vee\gamma\\ +\text{de Morgan's laws}:\quad & =\left(\neg\alpha\wedge\neg\beta\right)\vee\gunderline{\left(\alpha\wedge\neg\gamma\right)}\vee\gunderline{\left(\beta\wedge\neg\gamma\right)}\vee\gamma\\ +\text{identity }p\vee(\neg p\wedge q)=p\vee q:\quad & =\gunderline{\left(\neg\alpha\wedge\neg\beta\right)\vee\alpha}\vee\beta\vee\gamma\\ +\text{identity }p\vee(\neg p\wedge q)=p\vee q:\quad & =\gunderline{\neg\alpha\vee\alpha}\vee\beta\vee\gamma\\ +\text{axiom ``use arg''}:\quad & =True\quad. \end{align*} \end_inset -Substituting this proof into the proof tree, we shorten the tree to: +Since each proof rule of the constructive logic is translated into a true + formula in Boolean logic, it follows that a proof tree in the constructive + logic will be translated into a tree of Boolean formulas that have value + +\begin_inset Formula $True$ +\end_inset + + for each axiom or proof rule. + The result is that any constructive proof for a sequent such as +\begin_inset Formula $\emptyset\vdash f(\alpha,\beta,\gamma)$ +\end_inset + + is translated into a chain of Boolean implications that look like this: \begin_inset Formula \[ -\xymatrix{\xyScaleY{1.5pc}\xyScaleX{3.5pc}\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}} & \square} +True=(...)\Rightarrow(...)\Rightarrow...\Rightarrow f(\alpha,\beta,\gamma)\quad. \] \end_inset +Since +\begin_inset Formula $\left(True\Rightarrow\alpha\right)=\alpha$ +\end_inset + +, this chain proves the Boolean formula +\begin_inset Formula $f(\alpha,\beta,\gamma)$ +\end_inset +. \end_layout \begin_layout Standard -It remains to obtain the proof of -\begin_inset Formula $S_{0}$ +For example, the proof tree shown in Figure +\begin_inset space ~ \end_inset - by applying the proof transformer of the rule -\begin_inset Quotes eld -\end_inset -( -\begin_inset Formula $\text{Right}\Rightarrow$ -\end_inset +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Proof-of-the-sequent-example-2" +plural "false" +caps "false" +noprefix "false" -) -\begin_inset Quotes erd \end_inset -: + is translated into: \begin_inset Formula \begin{align*} - & \text{Proof}\,(S_{0})=\text{Proof}\,(\emptyset\vdash(\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta)\\ - & =q^{:(A\rightarrow A)\rightarrow B}\rightarrow\text{Proof}\,(S_{1})_{\text{given }q^{:C}}=q^{:(A\rightarrow A)\rightarrow B}\rightarrow q(x^{:A}\rightarrow x)\quad. +\text{axiom ``use arg''}:\quad & True=\left(\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\neg\alpha\vee\alpha\right)\\ +\text{rule ``create function''}:\quad & \quad\Rightarrow\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\alpha\right)\quad.\\ +\text{axiom ``use arg''}:\quad & True=\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\quad.\\ +\text{rule ``use function''}:\quad & True\Rightarrow\left(\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\beta\right)\\ +\text{rule ``create function''}:\quad & \quad\Rightarrow\left(\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\Rightarrow\beta\right)\quad. \end{align*} \end_inset -The proof tree is now shortened to a single code expression, -\begin_inset Formula $q^{:(A\rightarrow A)\rightarrow B}\rightarrow q(x^{:A}\rightarrow x)$ -\end_inset - -, that serves as an evidence of proof for -\begin_inset Formula $S_{0}$ -\end_inset - because this code has type -\begin_inset Formula $\left(\left(A\rightarrow A\right)\rightarrow B\right)\rightarrow B$ -\end_inset +\end_layout -. - In this way, we have derived the code of a fully parametric function from - its type signature. +\begin_layout Standard +It is easier to check Boolean truth tables than to find a proof tree in + constructive logic (or to establish that no proof tree exists). + If we find that a formula is +\emph on +not +\emph default + true in Boolean logic, we know it is also not true in constructive logic. + This gives us a quick way of proving that some type signatures are +\emph on +not +\emph default + implementable as fully parametric functions. + However, if a formula is true in Boolean logic, it does not follow that + the formula is also true in the constructive logic. \end_layout \begin_layout Standard -Figure +In addition to formulas shown in Table \begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "fig:proof-transformers-for-LJ-rules" +reference "tab:Logical-formulas-not-Boolean-theorems" plural "false" caps "false" noprefix "false" \end_inset - shows the proof transformers for all the rules of the LJ algorithm. - Apart from the special rule -\begin_inset Quotes eld + (Section +\begin_inset space ~ \end_inset -( -\begin_inset Formula $\text{Left}\Rightarrow$ + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Motivation-and-first-examples" +plural "false" +caps "false" +noprefix "false" + \end_inset -) -\begin_inset Quotes erd +), here are three more examples of formulas that are +\emph on +not +\emph default + true in Boolean logic: +\begin_inset Formula +\[ +\forall\alpha.\,\alpha\quad,\quad\quad\forall(\alpha,\beta).\,\alpha\Rightarrow\beta\quad,\quad\quad\forall(\alpha,\beta).\,(\alpha\Rightarrow\beta)\Rightarrow\beta\quad. +\] + \end_inset -, all other rules have proof transformers using just one of the code constructio -ns ( -\begin_inset Quotes eld +These formulas are also +\emph on +not +\emph default + true in the constructive logic. +\end_layout + +\begin_layout Subsection +The constructive logic and the law of excluded middle +\end_layout + +\begin_layout Standard +Computations in the Boolean logic are often performed using truth tables. + It is perhaps surprising that the proof rules of the constructive logic + are +\emph on +not +\emph default + equivalent to checking whether some propositions are +\begin_inset Formula $True$ \end_inset -create function -\begin_inset Quotes erd + or +\begin_inset Formula $False$ \end_inset -, -\begin_inset Quotes eld + via a truth table. + A general form of this statement was proved by K. +\begin_inset space ~ \end_inset -create tuple +G +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash + \begin_inset Quotes erd \end_inset -, -\begin_inset Quotes eld +o +\end_layout + \end_inset -use tuple +del +\begin_inset Index idx +status open + +\begin_layout Plain Layout +Kurt@Kurt G +\begin_inset ERT +status open + +\begin_layout Plain Layout + + +\backslash + \begin_inset Quotes erd \end_inset -, etc.) allowed within fully parametric code. -\end_layout - -\begin_layout Subsubsection* -The LJT algorithm +o \end_layout -\begin_layout Standard -As we have seen, the LJ algorithm enters a loop if the rule -\begin_inset Quotes eld \end_inset -( -\begin_inset Formula $\text{Left}\Rightarrow$ -\end_inset +del +\end_layout -) -\begin_inset Quotes erd \end_inset - gives a sequent we already had at a previous step. - That rule requires us to prove two new sequents: -\begin_inset Formula -\[ -\frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,A\Rightarrow B\vdash C}~(\text{Left}\Rightarrow)\quad. -\] + in 1932. +\begin_inset Foot +status open -\end_inset +\begin_layout Plain Layout +See +\family typewriter -A sign of trouble is that the first of these sequents ( -\begin_inset Formula $\Gamma,A\Rightarrow B\vdash A$ -\end_inset +\begin_inset CommandInset href +LatexCommand href +name "plato.stanford.edu/entries/intuitionistic-logic-development/" +target "https://plato.stanford.edu/entries/intuitionistic-logic-development/#SomeEarlResu" +literal "false" -) does not have a simpler form than the initial sequent ( -\begin_inset Formula $\Gamma,A\Rightarrow B\vdash C$ \end_inset -). - So, it is not clear that we are getting closer to completing the proof. - If -\begin_inset Formula $A=C$ -\end_inset -, the new sequent will simply repeat the initial sequent, immediately creating - a loop. \end_layout -\begin_layout Standard -In some cases, a repeated sequent will occur after more than one step. - It is not easy to formulate rigorous conditions for stopping the loop or - for avoiding the rule -\begin_inset Quotes eld \end_inset -( -\begin_inset Formula $\text{Left}\Rightarrow$ + In this sense, constructive logic does +\emph on +not +\emph default + imply that every proposition is either +\begin_inset Formula $True$ \end_inset -) -\begin_inset Quotes erd + or +\begin_inset Formula $False$ \end_inset . + This is not intuitive and requires getting used to. + Reasoning in the constructive logic must use the axioms and derivation + rules directly, instead of truth tables. \end_layout \begin_layout Standard -The LJT algorithm solves this problem by removing the rule -\begin_inset Quotes eld +The reason Boolean logic can use truth tables is that every Boolean proposition + is either +\begin_inset Formula $True$ \end_inset -( -\begin_inset Formula $\text{Left}\Rightarrow$ + or +\begin_inset Formula $False$ \end_inset -) -\begin_inset Quotes erd +. + This can be written as the formula +\begin_inset Formula $\forall\alpha.\,(\neg\alpha\vee\alpha=True)$ \end_inset - from the LJ algorithm. - Instead, -\emph on -four -\emph default - new rules are introduced. - Each of these rules contains a different pattern instead of -\begin_inset Formula $A$ +. + Table +\begin_inset space ~ \end_inset - in the premise -\begin_inset Formula $A\Rightarrow C$ -\end_inset -: -\begin_inset Formula -\begin{align*} -\text{(}A\text{ is atomic)\,}\frac{\Gamma,A,B\vdash D}{\Gamma,A,{\color{blue}A\Rightarrow B}\vdash D}~(\text{Left}\Rightarrow_{A})\qquad & \qquad\frac{\Gamma,A\Rightarrow B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\wedge B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\wedge})\\ -\frac{\Gamma,B\Rightarrow C\vdash A\Rightarrow B\quad\quad\Gamma,C\vdash D}{\Gamma,{\color{blue}(A\Rightarrow B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\Rightarrow})\qquad & \qquad\frac{\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\vee B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\vee}) -\end{align*} +\begin_inset CommandInset ref +LatexCommand ref +reference "tab:Proof-rules-of-constructive-and-boolean" +plural "false" +caps "false" +noprefix "false" \end_inset -The rule + uses the Boolean identity +\begin_inset Formula $\left(\alpha\Rightarrow\beta\right)=(\neg\alpha\vee\beta)$ +\end_inset + +, which does not hold in the constructive logic, to translate the constructive + axiom \begin_inset Quotes eld \end_inset -\begin_inset Formula $\text{Left}\Rightarrow_{A}$ +\begin_inset Formula $\text{use arg}$ \end_inset \begin_inset Quotes erd \end_inset - applies only if the implication starts with an -\begin_inset Quotes eld + into the Boolean axiom +\begin_inset Formula $\neg\alpha\vee\alpha=True$ \end_inset -atomic -\begin_inset Quotes erd +. + The formula +\begin_inset Formula $\forall\alpha.\,\neg\alpha\vee\alpha=True$ \end_inset - type expression, i.e., a single type parameter or a unit type. - In all other cases, the implication must start with a conjunction, a disjunctio -n, or an implication, which means that one of the three remaining rules - will apply. -\end_layout + is known as the +\series bold +law of excluded middle +\series default +. +\begin_inset Foot +status open + +\begin_layout Plain Layout +See +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +name "https://en.wikipedia.org/wiki/Law_of_excluded_middle" +target "https://en.wikipedia.org/wiki/Law_of_excluded_middle" +literal "false" -\begin_layout Standard -The LJT algorithm retains all the rules in Figure -\begin_inset space ~ \end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:proof-transformers-for-LJ-rules" -plural "false" -caps "false" -noprefix "false" +\end_layout \end_inset - except the rule -\begin_inset Quotes eld -\end_inset -( -\begin_inset Formula $\text{Left}\Rightarrow$ -\end_inset +\begin_inset Index idx +status open + +\begin_layout Plain Layout +law of excluded middle +\end_layout -) -\begin_inset Quotes erd \end_inset -, which is replaced by the four new rules. - It is far from obvious that the new rules are equivalent to the old ones. - It took mathematicians several decades to come up with the LJT rules and - to prove their validity. - This book will rely on that result and will not attempt to prove it. + It is remarkable that the constructive logic +\emph on +does not have +\emph default + the law of excluded middle. + It is neither an axiom nor a derived theorem in constructive logic. \end_layout \begin_layout Standard -The proof transformers for the new rules are shown in Figure -\begin_inset space ~ +To see why, translate the constructive logic formula +\begin_inset Formula $\forall\alpha.\,\neg\alpha\vee\alpha=True$ \end_inset + into a type. + The negation operation ( +\begin_inset Formula $\neg\alpha$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:proof-transformers-for-LJT-rules" -plural "false" -caps "false" -noprefix "false" +) is defined as the implication +\begin_inset Formula $\alpha\Rightarrow False$ +\end_inset + +. + So, the formula +\begin_inset Formula $\forall\alpha.\,\neg\alpha\vee\alpha$ +\end_inset + corresponds to the type +\begin_inset Formula $\forall A.\,\left(A\rightarrow\bbnum 0\right)+A$ \end_inset . - Figures -\begin_inset space ~ + Can we compute a value of this type via fully parametric code? For that, + we need to compute either a value of type +\begin_inset Formula $A\rightarrow\bbnum 0$ \end_inset + or a value of type +\begin_inset Formula $A$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:proof-transformers-for-LJ-rules" -plural "false" -caps "false" -noprefix "false" +. + This decision needs to be made in advance independently of +\begin_inset Formula $A$ +\end_inset +, because the code of a fully parametric function must operate in the same + way for all types. + As we have seen in Example +\begin_inset space ~ \end_inset -– + \begin_inset CommandInset ref LatexCommand ref -reference "fig:proof-transformers-for-LJT-rules" +reference "subsec:ch-Example-type-identity-A-0" plural "false" caps "false" noprefix "false" \end_inset - define the set of proof transformers sufficient for using the LJT algorithm - in practice. - The -\begin_inset Index idx -status open +, a value of type +\begin_inset Formula $A\rightarrow\bbnum 0$ +\end_inset -\begin_layout Plain Layout + exists if the type +\begin_inset Formula $A$ +\end_inset -\family typewriter -curryhoward -\family default - library -\end_layout + is itself +\begin_inset Formula $\bbnum 0$ +\end_inset + +. + But we do not know in advance whether +\begin_inset Formula $A=\bbnum 0$ +\end_inset +. + Since there are no values of type +\begin_inset Formula $\bbnum 0$ \end_inset +, and the type parameter +\begin_inset Formula $A$ +\end_inset + could be, say, \begin_inset listings inline true status open \begin_layout Plain Layout -curryhoward +Int \end_layout \end_inset - library -\family typewriter +, we cannot compute a value of type +\begin_inset Formula $A\rightarrow\bbnum 0$ +\end_inset -\begin_inset Foot -status open +. +\end_layout -\begin_layout Plain Layout -See -\family typewriter +\begin_layout Standard +Why is it impossible to implement a value of the type +\begin_inset Formula $\left(A\rightarrow\bbnum 0\right)+A$ +\end_inset -\begin_inset CommandInset href -LatexCommand href -target "https://github.com/Chymyst/curryhoward" +? Surely, the type +\begin_inset Formula $A$ +\end_inset + + is either void or not void. + If +\begin_inset Formula $A$ +\end_inset + + is void then +\begin_inset Formula $\left(A\rightarrow\bbnum 0\right)\cong\bbnum 1$ +\end_inset + is not void (as Example +\begin_inset space ~ \end_inset -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:ch-Example-type-identity-A-0" +plural "false" +caps "false" +noprefix "false" \end_inset + shows). + So, one of the types in the disjunction +\begin_inset Formula $\left(A\rightarrow\bbnum 0\right)+A$ +\end_inset -\family default - implements these proof transformers. + should be non-void and have values that we can compute. \end_layout \begin_layout Standard -The most complicated of the new rules is the rule -\begin_inset Quotes eld +While this argument is true, it does not help implementing a value of type + +\begin_inset Formula $\left(A\rightarrow\bbnum 0\right)+A$ \end_inset -( -\begin_inset Formula $\text{Left}\Rightarrow_{\Rightarrow}$ + via fully parametric code. + It is not enough to know that one of the two values +\begin_inset Quotes eld \end_inset -) +should exist \begin_inset Quotes erd \end_inset . - Applying that rule's transformer results in evidence-of-proof code that - is longer than the code obtained via LJ's rule transformers. - The resulting code will need to be simplified symbolically. - -\end_layout - -\begin_layout Standard -As an example of using the LJT algorithm, we prove the sequent -\begin_inset Formula $S_{0}=\emptyset\vdash((\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta$ + We need to know +\emph on +which +\emph default + of the two values exists, and we need to write code that computes that + value. + That code may not decide what to do depending on whether the type +\begin_inset Formula $A$ \end_inset - from the previous section. - At each step, only one LJT rule applies to each sequent. - The initial part of the proof tree looks like this: -\begin_inset VSpace -100baselineskip% + is void, because the code must work in the same way for all types +\begin_inset Formula $A$ \end_inset + (void or not). + As we have seen, that code is impossible to write. +\end_layout -\begin_inset Formula -\[ -\xymatrix{\xyScaleY{1.5pc}\xyScaleX{6pc} & & & ~\\ -\ar[r]\sp(0.4){\emptyset\vdash((\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){(\alpha\Rightarrow\alpha)\Rightarrow\beta\vdash\beta} & (\text{Left}\Rightarrow_{\Rightarrow})\ar[ru]\sp(0.65){\beta\vdash\beta}\ar[r]\sp(0.65){\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha} & ~ -} -\] - +\begin_layout Standard +In Boolean logic, one may prove that a value +\begin_inset Quotes eld \end_inset -The proofs for the sequents -\begin_inset Formula $\beta\vdash\beta$ +should exist +\begin_inset Quotes erd \end_inset - and -\begin_inset Formula $\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha$ + by showing that the non-existence of a value is contradictory in some way. + However, any practically useful program needs to +\begin_inset Quotes eld \end_inset - are the same as before: -\begin_inset Formula -\[ -\text{Proof}\,(\beta\vdash\beta)_{\text{given }y^{:B}}=y\quad,\quad\quad\text{Proof}\,(\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha)_{\text{given }r^{:A\rightarrow B}}=x^{:A}\rightarrow x\quad. -\] - +construct +\begin_inset Quotes erd \end_inset -Substituting these proofs into the proof transformer of the rule + (i.e., to compute) actual values. + The \begin_inset Quotes eld \end_inset -( -\begin_inset Formula $\text{Left}\Rightarrow_{\Rightarrow}$ -\end_inset +constructive +\begin_inset Index idx +status open + +\begin_layout Plain Layout +constructive logic +\end_layout -) -\begin_inset Quotes erd \end_inset - produces this code: -\begin_inset Formula -\begin{align*} - & \text{Proof}\,((\alpha\Rightarrow\alpha)\Rightarrow\beta\vdash\beta)_{\text{given }q^{:(A\rightarrow A)\rightarrow B}}=q\big(\text{Proof}\,(\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha)_{\text{given }r^{:A\rightarrow B}}\big)\\ - & \quad\quad\text{where }r^{:A\rightarrow B}=a^{:A}\rightarrow q(\_^{:A}\rightarrow a)\\ - & =q(x^{:A}\rightarrow x)\quad. -\end{align*} +\begin_inset Quotes erd \end_inset -The proof of -\begin_inset Formula $\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha$ -\end_inset + logic got its name from this requirement. + So, it is the constructive logic (not the Boolean logic) that provides + correct reasoning about the types of values computable by fully parametric + functional programs. +\end_layout - does not actually use the intermediate value -\begin_inset Formula $r^{:A\rightarrow B}$ -\end_inset +\begin_layout Standard +If we drop the requirement of full parametricity, we +\emph on +could +\emph default + implement the law of excluded middle +\begin_inset Index idx +status open + +\begin_layout Plain Layout +law of excluded middle +\end_layout - provided by the proof transformer. - As a symbolic simplification step, we may simply omit the code of -\begin_inset Formula $r$ \end_inset . - The + Special features of Scala (reflection, type tags, and type casts) allow + programmers to compare types as values and to determine what type was given + to a type parameter when a function is applied: \begin_inset listings -inline true +lstparams "mathescape=true" +inline false status open \begin_layout Plain Layout -curryhoward +import scala.reflect.runtime.universe._ \end_layout -\end_inset +\begin_layout Plain Layout - library always performs symbolic simplification after applying the LJT - algorithm. - + // Convert the type parameter T into a special value. \end_layout -\begin_layout Standard -\begin_inset Float figure -wide false -sideways false -status open - \begin_layout Plain Layout -\align center -\begin_inset Box Boxed -position "t" -hor_pos "c" -has_inner_box 1 -inner_pos "t" -use_parbox 0 -use_makebox 0 -width "100col%" -special "none" -height "1in" -height_special "totalheight" -thickness "0.4pt" -separation "3pt" -shadowsize "4pt" -framecolor "black" -backgroundcolor "none" -status open + +def getType[T: TypeTag]: Type = weakTypeOf[T] +\end_layout \begin_layout Plain Layout -\begin_inset Formula -\begin{align*} -\frac{\Gamma,A,B\vdash D}{\Gamma,A,{\color{blue}A\Rightarrow B}\vdash D}~(\text{Left}\Rightarrow_{A})\quad & \quad\text{Proof}\,(\Gamma,A,A\Rightarrow B\vdash D)_{\text{given }p^{:\Gamma},x^{:A},q^{:A\rightarrow B}}\\ - & \quad\quad=\text{Proof}\,(\Gamma,A,B\vdash D)_{\text{given }p,x,q(x)}\\ -\frac{\Gamma,A\Rightarrow B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\wedge B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\wedge})\quad & \quad\text{Proof}\,(\Gamma,(A\wedge B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:A\times B\rightarrow C}}\\ - & \quad\quad=\text{Proof}\,(\Gamma,A\Rightarrow B\Rightarrow C\vdash D)_{\text{given }p,(a^{:A}\rightarrow b^{:B}\rightarrow q(a\times b))}\\ -\frac{\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\vee B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\vee})\quad & \quad\text{Proof}\,(\Gamma,(A\vee B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:A+B\rightarrow C}}\\ - & \quad\quad=\text{Proof}\,(\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D)_{\text{given }p,r,s}\\ - & \quad\quad\text{where}~r\triangleq a^{:A}\rightarrow q(a+\bbnum 0)\text{ and }s\triangleq b^{:B}\rightarrow q(\bbnum 0+b)\\ -\frac{\Gamma,B\Rightarrow C\vdash A\Rightarrow B\quad\quad\Gamma,C\vdash D}{\Gamma,{\color{blue}(A\Rightarrow B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\Rightarrow})\quad & \quad\text{Proof}\,(\Gamma,(A\Rightarrow B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:\left(A\rightarrow B\right)\rightarrow C}}\\ - & \quad\quad=\text{Proof}\,(\Gamma,C\vdash D)_{\text{given }p,c}\\ - & \quad\quad\text{ where}~c^{:C}\triangleq q\big(\text{Proof}\,(\Gamma,B\Rightarrow C\vdash A\Rightarrow B)_{\text{given }p,r}\big)\\ - & \quad\quad\text{ and }r^{:B\rightarrow C}\triangleq b^{:B}\rightarrow q(\_^{:A}\rightarrow b) -\end{align*} -\end_inset + // Compare types A and B. +\end_layout +\begin_layout Plain Layout +def equalTypes[A: TypeTag, B: TypeTag]: Boolean = getType[A] =:= getType[B] \end_layout -\end_inset - +\begin_layout Plain Layout \end_layout \begin_layout Plain Layout -\begin_inset Caption Standard -\begin_layout Plain Layout -\begin_inset CommandInset label -LatexCommand label -name "fig:proof-transformers-for-LJT-rules" + // excludedMiddle has type ${ +\backslash +color{dkgreen} +\backslash +forall A. +\backslash +left(A +\backslash +rightarrow +\backslash +bbnum 0 +\backslash +right)+A}$. +\end_layout -\end_inset +\begin_layout Plain Layout -Proof transformers for the four new rules of the -\begin_inset Index idx -status open +def excludedMiddle[A: TypeTag]: Either[A, A => Nothing] = +\end_layout \begin_layout Plain Layout -LJT algorithm|textit + + if (equalTypes[A, Nothing]) Right((identity _).asInstanceOf[A => Nothing]) + // Return ${ +\backslash +color{dkgreen} +\backslash +text{id}: +\backslash +bbnum 0 +\backslash +rightarrow +\backslash +bbnum 0}$. \end_layout -\end_inset +\begin_layout Plain Layout -LJT algorithm. + else if (equalTypes[A, Int]) Left(123.asInstanceOf[A]) // Produce + some value of type Int. \end_layout -\end_inset - +\begin_layout Plain Layout + else if (equalTypes[A, Boolean]) Left(true.asInstanceOf[A]) // Produce + some value of type Boolean. \end_layout -\end_inset - +\begin_layout Plain Layout + else ??? // Write more definitions to support all other Scala + types. \end_layout -\begin_layout Standard -The reason the LJT algorithm terminates is that each rule replaces a given - sequent by one or more sequents with simpler premises or goals. -\begin_inset Foot -status open - \begin_layout Plain Layout -The paper -\family typewriter - -\begin_inset CommandInset href -LatexCommand href -target "http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.35.2618" -literal "false" -\end_inset +\end_layout +\begin_layout Plain Layout -\family default - shows that the LJT algorithm terminates by giving an explicit decreasing - measure on proof trees. +scala> excludedMiddle[Int] \end_layout -\end_inset - - This guarantees that the proof search will terminate either with a complete - proof or with a sequent to which no more rules apply. - An example of such a -\begin_inset Quotes eld -\end_inset +\begin_layout Plain Layout -dead-end -\begin_inset Quotes erd -\end_inset +res0: Either[Int,Int => Nothing] = Left(123) +\end_layout - sequent is -\begin_inset Formula $\alpha\vdash\beta$ -\end_inset +\begin_layout Plain Layout - where -\begin_inset Formula $\alpha$ -\end_inset +\end_layout - and -\begin_inset Formula $\beta$ -\end_inset +\begin_layout Plain Layout - are different, unrelated propositions. - In that situation, the LJT algorithm concludes that the initial sequent - cannot be proved. +scala> excludedMiddle[Nothing] \end_layout -\begin_layout Standard -\begin_inset Note Note -status collapsed - \begin_layout Plain Layout -Rule -\begin_inset Formula $L\rightarrow_{4}$ -\end_inset - is based on the key theorem: -\size footnotesize +res1: Either[Nothing,Nothing => Nothing] = Right() +\end_layout -\begin_inset Formula -\[ -\left(\left(A\rightarrow B\right)\rightarrow C\right)\rightarrow\left(A\rightarrow B\right)\,\Longleftrightarrow\,\left(B\rightarrow C\right)\rightarrow\left(A\rightarrow B\right) -\] +\end_inset +In this code, we check whether +\begin_inset Formula $A=\bbnum 0$ \end_inset +. + If so, we can implement +\begin_inset Formula $A\rightarrow\bbnum 0$ +\end_inset -\end_layout + as an identity function of type +\begin_inset Formula $\bbnum 0\rightarrow\bbnum 0$ +\end_inset -\begin_layout Plain Layout -The key theorem for rule -\begin_inset Formula $L\rightarrow_{4}$ +. + Otherwise, we know that +\begin_inset Formula $A$ \end_inset - is attributed to Vorobieff (1958) -\end_layout + is one of the existing Scala types ( +\begin_inset listings +inline true +status open \begin_layout Plain Layout -A stepping stone to this theorem: -\size footnotesize -\begin_inset Formula -\[ -\left(\left(A\rightarrow B\right)\rightarrow C\right)\rightarrow B\rightarrow C -\] +Int +\end_layout \end_inset +, +\begin_inset listings +inline true +status open -\size default -Proof: -\begin_inset Formula $f^{:\left(A\rightarrow B\right)\rightarrow C}\rightarrow b^{:B}\rightarrow f\,(\_^{:A}\rightarrow b)$ -\end_inset - +\begin_layout Plain Layout +Boolean \end_layout -\begin_layout Plain Layout -\begin_inset Separator parbreak \end_inset - +, etc.), which are not void and have values that we can simply write down + one by one in the subsequent code. + \end_layout -\begin_layout Plain Layout -Proof search V: From deduction rules to code -\end_layout +\begin_layout Standard +Explicit +\begin_inset Index idx +status open \begin_layout Plain Layout -The new rules are equivalent to the old rules, therefore... +type casts \end_layout -\begin_layout Plain Layout -Proof of a sequent -\begin_inset Formula $A,B,C\vdash G$ \end_inset -\begin_inset Formula $\Leftrightarrow$ -\end_inset - - code/expression -\begin_inset Formula $t(a,b,c):G$ -\end_inset - - -\end_layout +\series bold +type casts +\series default +, such as +\begin_inset listings +inline true +status open \begin_layout Plain Layout -Also can be seen as a function -\begin_inset Formula $t$ -\end_inset - from -\begin_inset Formula $A,B,C$ -\end_inset +123.asInstanceOf[A] +\end_layout - to -\begin_inset Formula $G$ \end_inset - -\end_layout +, are needed because the Scala compiler cannot know that +\begin_inset listings +inline true +status open \begin_layout Plain Layout -Sequent in a proof follows from an axiom or from a transforming rule + +A \end_layout -\begin_layout Plain Layout -The two axioms are fixed expressions, -\begin_inset Formula $x^{A}\rightarrow x$ \end_inset - and -\begin_inset Formula $1$ -\end_inset + is +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +Int \end_layout -\begin_layout Plain Layout -Each rule has a -\emph on -proof transformer -\emph default - function: -\begin_inset Formula $\text{PT}_{R\rightarrow}$ -\end_inset - - , -\begin_inset Formula $\text{PT}_{L+}$ \end_inset - , etc. -\end_layout + in the scope where we return +\begin_inset listings +inline true +status open \begin_layout Plain Layout -Examples of proof transformer functions: -\begin_inset Formula -\begin{align*} -\frac{\Gamma,A\vdash C\quad\;\Gamma,B\vdash C}{\Gamma,{\color{blue}A+B}\vdash C}\,L+\\ -PT_{L+}(t_{1}^{A\rightarrow C},t_{2}^{B\rightarrow C})=x^{A+B}\rightarrow & \ x\ \text{match}\begin{cases} -a^{A}\rightarrow t_{1}(a)\\ -b^{B}\rightarrow t_{2}(b) -\end{cases} -\end{align*} + +Left(123) +\end_layout \end_inset +. + Without a type cast, the compiler will not accept +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout -\begin_inset Formula -\begin{align*} -\frac{\Gamma,A\rightarrow B\rightarrow C\vdash D}{\Gamma,{\color{blue}(A\times B)\rightarrow C}\vdash D}\,L\rightarrow_{2}\\ -PT_{L\rightarrow_{2}}(f^{\left(A\rightarrow B\rightarrow C\right)\rightarrow D})=g^{A\times B\rightarrow C}\rightarrow & f\,(x^{A}\rightarrow y^{B}\rightarrow g(x,y)) -\end{align*} +123 +\end_layout \end_inset - -\end_layout + as a value of type +\begin_inset listings +inline true +status open \begin_layout Plain Layout -Verify that we can indeed produce PTs for every rule of LJT + +A \end_layout -\begin_layout Plain Layout -\begin_inset Separator parbreak \end_inset - + in that scope. \end_layout -\begin_layout Plain Layout -Proof search example II: deriving code -\end_layout +\begin_layout Standard +The method +\begin_inset listings +inline true +status open \begin_layout Plain Layout -Once a proof tree is found, start from leaves and apply PTs + +asInstanceOf \end_layout -\begin_layout Plain Layout -For each sequent -\begin_inset Formula $S_{i}$ \end_inset -, this will derive a -\series bold -proof expression -\series default - -\begin_inset Formula $t_{i}$ -\end_inset + is dangerous because the code +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +x.asInstanceOf[T] \end_layout -\begin_layout Plain Layout -Example: to prove -\begin_inset Formula $S_{0}$ \end_inset -, start from -\begin_inset Formula $S_{6}$ -\end_inset + disables the type checking for the value +\begin_inset listings +inline true +status open - backwards: -\size footnotesize +\begin_layout Plain Layout -\begin_inset Formula -\begin{align*} -S_{6}:\left(R\rightarrow R\right)\rightarrow Q;R\vdash R\quad(\text{axiom }Id)\quad & t_{6}(rrq,r)=r\\ -S_{2}:\left(R\rightarrow R\right)\rightarrow Q\vdash\left(R\rightarrow R\right)\quad\text{PT}_{R\rightarrow}(t_{6})\quad & t_{2}(rrq)=\left(r\rightarrow t_{6}(rrq,r)\right)\\ -S_{3}:Q\vdash Q\quad(\text{axiom }Id)\quad & t_{3}(q)=q\\ -S_{1}:\left(R\rightarrow R\right)\rightarrow Q\vdash Q\quad\text{PT}_{L\rightarrow}(t_{2},t_{3})\quad & t_{1}(rrq)=t_{3}(rrq(t_{2}(rrq)))\\ -S_{0}:\emptyset\vdash\left(\left(R\rightarrow R\right)\rightarrow Q\right)\rightarrow Q\quad\text{PT}_{R\rightarrow}(t_{1})\quad & t_{0}=\left(rrq\rightarrow t_{1}(rrq)\right) -\end{align*} +x +\end_layout \end_inset +. + This tells the Scala compiler to believe that +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +x \end_layout -\begin_layout Plain Layout -The proof expression for -\begin_inset Formula $S_{0}$ \end_inset - is then obtained as -\begin_inset Formula -\begin{align*} -t_{0} & =rrq\rightarrow t_{3}\left(rrq\left(t_{2}\left(rrq\right)\right)\right)=rrq\rightarrow rrq(r\rightarrow t_{6}\left(rrq,r\right)\\ - & =rrq\rightarrow rrq\left(r\rightarrow r\right) -\end{align*} + has type +\begin_inset listings +inline true +status open -\end_inset +\begin_layout Plain Layout -Simplified final code having the required type: -\begin_inset Formula -\[ -t_{0}:\left(\left(R\rightarrow R\right)\rightarrow Q\right)\rightarrow Q=\left(rrq\rightarrow rrq\left(r\rightarrow r\right)\right) -\] +T +\end_layout \end_inset + even when the type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +T \end_layout \end_inset -To -\emph on -prove -\emph default - that there is no proof, one needs to use methods that are beyond the scope - of this book. - An introduction to the required techniques is in the book -\begin_inset Quotes eld -\end_inset + is inconsistent with the actually given code of +\begin_inset listings +inline true +status open -Proof and Disproof in Formal Logic -\begin_inset Quotes erd -\end_inset +\begin_layout Plain Layout + +x +\end_layout - by R. -\begin_inset space ~ \end_inset -Bornat -\begin_inset Index idx -status open +. + The resulting programs compile but may give unexpected results or crash. + These errors would have been prevented if we did not disable the type checking. + In this book, we will avoid writing such code whenever possible. +\begin_inset Note Comment +status collapsed \begin_layout Plain Layout -Richard Bornat +showing here this is equivalent in Scala just different syntax importantly + non theorems cannot be implemented in code some on theorems are statements + in logic that cannot be derived statements that are false or undereye verbal + examples of these statements are these for all a from one follows a now + this is certainly suspicious in terms of logic what if a were false then + we would have it from true false false that's very obviously wrong and + we cannot implement a function of this type to implement it we would have + to take a unit argument and produce a value of type a where a is arbitrary + type but how can we produce a value of type a of the type that we don't + even know what it is and there is no data for us to produce that value + so it is impossible another example of an impossible type is this type + so from a plus B follows a if you wanted to implement this function you + would have to take a value of disjunction type a plus B and return a value + of type a but how can you do that what exodus Junction type happens to + contain B and no a just B it cannot contain a if it contains a B it's a + disjunction so then we don't have an A and then we again cannot produce + any and having a B which is a completely different arbitrary type doesn't + help us to produce me exactly the same reason shows why we cannot produce + an A a and B given a because that requires a B we cannot produce and also + this is not implementable because we are required to produce an A but all + we have is a function from A to B this function will consume an A if given + only this function cannot possibly produce an A for us but we are required + to produce an A as a result so we cannot and also there is no proof of + this formula in the logic so these examples actually lead us to a natural + question how can we decide given a certain formula whether it is a theorem + in logic and therefore whether it can be implemented in code it is not + obvious consider this example can we write a function with this type in + Scala it is not obvious can we prove this formula it is not clear not quite + obvious right now suppose I were of the opinion that this cannot be proved + but how do I show that this cannot be proved I certainly cannot just try + all possible proofs that would be infinitely many possible proofs that + would give me all kinds of other formulas and that would give me nothing + that I can stand oh how to answer these questions so it is really a very + hard question we are not going to try to answer it on our own we were going + to use the results of mathematicians they have studied these questions + for many many years for centuries logic has been studied since ancient + Greece more than 2,000 years of study all we need to do is to find out + by what name mathematicians call this logic they are probably already studied + it what kind of logic is this that we are using that follows from the type + constructions remember and the very beginning of our consideration we started + with the type constructions that our programming languages have so that's + set of type constructions specifies the set of rules of derivation of the + logic mathematicians call this logic intuitionistic propositional logic + or IPL also they call it constructive propositional logic but it is less + frequently used most frequently used name is this and mathematicians also + call this a non classical logic because this logic is actually different + from the boolean logic that we are familiar with the logic of the values + true and false and their truth tables I assume that you are familiar with + those computations using truth tables and operations and or not in the + boolean logic so actually this logic the logic of types as I call it or + intuitionistic propositional logic is very different from boolean logic + in certain ways it's similar in other ways disjunction for instance works + very differently here's an example consider this sequence if it has given + that from a follows B plus C then either from a follows B or from a follows + C it sounds right from the common-sense point of it if if B plus C Falls + a B or C if I was I'm using plus as a logical or so if B or C follows then + it kind of makes sense either B follows or C Falls indeed this is correct + in the boolean logic which we can find out by writing the truth table so + we enumerate all the possibilities for a B and C to be true or false or + eight such possibilities and for each of those possibilities we write the + truth value of this the truth value of this and we see from the table that + whenever this is true then this is also true in the boolean logic but this + does not hold in the intuitionistic logic for the logic of types well why + does it not hold that's counterintuitive well in fact there is very little + that's intuitive about this so-called intuitionistic logic actually we + need to think differently about this logic we need to think can we implement + an expression of this sequent so implementing it would mean if we're given + this expression we can build an expression of this type so we're given + an expression of type A to B plus C let's say some F of this type can we + build an expression of this type we can this differently by asking can + we implement a function that takes this as an argument and returns this + well we know that this is equivalent one of our derivation rules is that + if you have this sequence then you can also have a sequence that is a function + type from this to this so for the programmer it is easier to reason about + a function taking this as an argument and returning this so how can we + implement this function this function takes F and needs to return a value + of this type so the body of this function if we could implement it and + have to construct a value of type either of something there are only two + ways of constructing a value of type either one is to construct the left + value second is to construct the right value how do we decide whether to + construct the left value or the right value we have to decide it somehow + on the basis of what information can we decide it we don't actually have + any such information what we have here is a function from a to either BC + so given some value of a of type a we could compute f of that value and + then we would have either B or C we could decide them whether to we could + take them that B or that C but that's not what we need to return we don't + need to return either of BC we need to return either of this function or + that function and that function is not yet applied to any a it is it is + too late for us to ask what is the a we already have to return the left + of this or a right of that in other words this type either of something-somethi +ng is not itself a function of a it contains functions away but itself it + cannot be decided on the basis of any assets too late so we need to supply + a left or right so here right away immediately we have to decide whether + this will return a left or a right and we cannot really decide that if + we decide we return the left we must then return a function from A to B + so there's no way for us to construct this function if we're given this + function because this function could sometimes return C instead of B and + then we'll be stuck we cannot do this and we can also return we cannot + also return the right either so it is impossible to implement a function + of this type implication also works a little differently in the intuitionistic + logic here's an example this holds in boolean logic but not in intuitionistic + logic again let's see why how can we compute this given this this function + will give us an e only when given an argument of this type but how can + we produce a value of this type we cannot we don't have information that + will allow us to produce a value of this type a and B are some arbitrary + types remember there is universal quantifier outside of all this for all + a and for all B we're supposed to produce this and that is impossible we + don't have enough data to produce some values type a and so we cannot implement + this function conjunction works kind of the same as in boolean logic so + here's an example this implemented and this is also in boolean logic a + true theorem now in boolean logic the usual way of deciding whether something + is true or something is a theorem is to write a truth table unfortunately + the intuitionistic logic cannot have a truth table it cannot have a fixed + number of truth values even if you allow more than two truth values such + that the validity of formulas the truth of theorems can be decided on the + basis of the truth table this was shown by noodle and this means we should + not actually try to reason about this logic using truth values it is not + very useful even an infinite infinite number of truth values will not help + instead however it turns out that this logic has a decision procedure or + an algorithm and this algorithm is guaranteed either to find the proof + for any given formula of the internation intuitionistic logic or to determine + that there is no proof for that formula the algorithm can also find several + in equivalent proofs if there is a theorem so a theorem could have several + in equivalent proofs and since each proof could be automatically translated + into code of that type it means we could generate several in equivalent + expressions of some type sometimes so that is the situation with this logic + which we discover if we write if we read papers about intuitionistic propositio +nal logic that are available in the literature and their open source projects + on the web such as the gen GHC which is a compiler plugin for haskell this + is another project doing the same thing and for Scala are implemented occurred + the Clary Howard library both of these Scala and Haskell all of these color + and Haskell projects do the same thing they take a type of some expression + for function and generate code for it automatic by translating the type + into sequence finding a proof in this logic using the algorithm and translating + that proof back into code in the way that we have seen in an example it + is interesting that all these provers and there's a few others there's + one more for the idris language I did not mention here they all used the + same decision procedure or the same basic algorithm which is called ljt + which was explained in a paper by dick off here they all side the same + paper and I believe this is so because most other papers on this subject + are unreadable to non-specialists they are written in a very complicated + way or they describe algorithms that are too complicated so I will show + how this works in the rest of this tutorial in order to find out how to + get an algorithm we need to ask well first of all do we have the rules + of derivation that allow us to create an algorithm already here is a summary + of the axioms and the rules of derivation that we have found so far these + are direct translations of the cold expressions that we held in the programming + language in the notation of sequence now there's one other notation for + derivation rules which looks like a fraction like this the numerator is + one or more sequins and the denominator is a sequence and this notation + means in order to derive what is in the denominator you have to present + proofs for what is in the numerator so this is the convention in the literature + this fraction like syntax or notation now we keep in mind that proofs of + sequence are actually just called expressions that have these types as + some variables and this type is the entire expression so these are directly + responding to proofs of this sequence and to the proofs of these derivation + rules and so if we have a proof that operates by combining some of these + axioms and some of these generation rules which directly translate that + back into code now the question is do these rules give us an algorithm + for finding a proof the answer is no how can we use these rules to obtain + an algorithm well suppose we need to prove some sequence like this in order + to prove it we could first see if the sequence is one of the axioms if + so then we have already proved if we know what expression to write now + in this case none of the axioms match this so much means maybe a is a times + B so B here is C and then on the Left we must have C or you must have a + times B now we don't you don't have C on the left as we have because even + that's not the same we also don't have a times B at the premise we have + a but we don't have a times B so these rules don't match the other rules + don't match the premises and the goal either but also these rules so how + can we use them well when the writer must be an implication we don't have + an application on the right here we could try to delete some of the premises + because it's unused well actually it doesn't look like a good idea could + you read a for example and we end up with an really hopeless sequence from + B plus C we cannot get an A ever and so but sounds hopeless so this doesn't + seem to help and changing the order doesn't seem to help much either and + so we cannot find matching rules but actually this sequence is provable + just a clever combination of what axiom to start with and what role to + use and then again some axiom and so on it will give us that time sure + because I know how to write code for this this is not difficult you have + a function with two arguments one of them is a the other is B plus C so + disjunction of either B C and we are supposed to produce a disjunction + of tuple a B or C that's easy look at this disjunction if we have a B in + this disjunction then we can produce a left of the tuple a B because we + always have an A anyway if we have a see in this disjunction then we could + return this part of the disjunction in the right of C and we're done but + unfortunately we see that the rules here do not give us an algorithm for + deciding this we need a better formulation of the logic again mathematicians + need to save us from the situation and they have done so mathematicians + have studied this logic for a long time starting from the early 20th of + the last century the first algorithmic formulation of the logic that was + found is due to Jensen who published what he called the calculus just ignore + the word calculus it means not very much complete and sound calculus means + that he came up with some rules of derivation which are summarized here + such that they are equivalent to these they derive all the same theorems + and only the same theorems so they derive all the stuff that is right and + only that stuff they don't derive any wrong statements it's very hard to + come up with such a system of axioms and derivation rules that are equivalent + to another one in this sense also it's very hard to prove that these are + actually the rules that will give you all the theorems that could be right + in this logic that you can actually derive all the theorems that are right + yet work is already done by mathematicians so we're not going to try to + do it ourselves we're just going to understand how these rules work now + the syntax here is slightly enhanced compared with this the enhancement + is that their names pretty cool now these are just labels they don't really + do anything in terms of sequence these help us identify which we all have + has been applied to which sequence and that's all we do so other than that + it is the same notation so the fraction such as this one means that there + is a sequence in the denominator which we will prove if there are proofs + given for sequence in the numerator in this rule there are two sequence + of them in the numerator other rules may have one sequence in the numerator + or no sequence in the numerator so these rules that will have no previous + sequence required those are axioms this axiom means if you have an atomic + X in other words it's a variable it's a type variables not not a complicated + expression just attack variable and you can derive that same variable this + is our accion right here now why is it important that this is atomic that + this is type variable and not a more complicated expression actually not + important but it's the simplest rule that you can come up with and mathematicia +ns always like the most minimal set of rules so that's why they say let's + only consider this rule for the type variables X not for more complicated + expressions but we can consider this rule for any expression of course + the identity axiom well here is a truth truth axiom net which derives the + truth which is the ste symbol which I denote it by one the format in logical + notation this is the T symbol well let's just call this one for clarity + so that can be derived from any premises with no previous sequence necessary + none of these other rules now what do these other rules do they do an interesti +ng thing actually each of these rules is either about something in the sequence + on the left to the trans time or something in the sequence to the right + of the transplant which I here shown in blue so these are the interesting + parts of the sequence that are being worked on or transformed by the rule + so here's an example this rule is actually two rules the eyes the index + so I is one or two another two rules just written for gravity like this + with index I and each of them says you will prove this if you prove one + of if you prove this so for example you will prove C given if you're given + a a one A two if you will prove C given just a one which makes sense because + if you can prove C given a one you don't need a two we can ignore this + a T we can already proved C from anyone so in this way it would be proved + and so all these rules work in this way you can prove what's on the bottom + of the seat of the of the fraction if you're given proofs for what's on + the top so these are eight derivation rules and two axioms we can use this + now to make a proof search how do we do that I start with a sequence we + see which rule matches that sequence so the sequence must have something + on the left and something on the right well at least one of these it cannot + be empty so it must be something somewhere and there are only four kinds + of expressions in our logic type variables conjunctions implications and + disjunctions now notice I'm using this arithmetic arithmetic all notation + for logic just because I like it better and I will show that it has advantages + later so we take a sequence we see which rule matches one of them won't + match because either in the premise we have one of these expressions were + in the goal we have one of these expressions and then we find the rule + of match that matches we apply that rule so we now have new sequence one + or more that we will need to be proved and if they're true then we fork + the tree and now we have to prove both of them son-in we continue doing + that for each of the sequence until we hit axioms so the tree will and + this leaf or we hit a sequence to which no rule applies in which case we + cannot prove it and the entire thing is unprovable so in the search tree + there will be sequence at the nodes of the tree and proofs will be at the + edges of the tree so each node sends its proof to the root of the tree + this calculus is guaranteed by mathematicians to be such that indeed if + you cannot find a rule that applies that means the sequence cannot be proved + which was not the case here the sequence can be proved and yet we cannot + find a rule that applies so in this calculus we can use bottom-up approach + to make a proof search as a tree here we cannot that is the advantage capitaliz +ing on the mathematicians results let us look at an example suppose we want + to prove this formula this theorem so first step we need to write a sequence + and this needs to be proved from no premises so we write a sequence s0 + which has an empty set of premises this is a single now what rule applies + to this sequence with your bottom up so in other words we look at these + rules and they refine which denominator matches our sequential and our + cylinders empty set on the left so all the rules on the left cannot be + applied but on the right we have an expression which is an implication + at the top level of this expression there is this implies that so this + is of the form a implies B so this rule applies we have a sequence of the + form something in our case this is an empty set and then a implies B so + we apply this rule which is the right implication and we get a new sequence + which is that what was here before the implication is now put on the left + to the trans of the to the left of the trans time and it means that this + expression needs to be now to the left of the turnstile so now this is + the sequence s1 now we need to prove s1 well we see what rule applies to + us one well on the right there is just Q so nothing can be done of these + rules and Q is not truth so we cannot use the axiom either so let's look + at their left rules on the Left we have now an implication so this is let's + say a and this is B so we have a rule which has a implication B on the + left this is the row left implication let's apply it that law will give + us two new sequence so these two new sequence are s2 and s3 no these ones + as you can check if you match a location B against this implication Q so + this is a this is B so then you get these two sequence now we have to prove + these two sequence as 2 and s 3 s 3 is easy it is just the axiom of identity + it is this now as 2 again has an implication on the left let's again apply + the rule left implication to that we get two more sequence as foreign s5 + as for is this because 5 is this so now actually we are in trouble because + as 2 and s 4 is are the same sequence as 5 actually we could prove with + some more work but that won't help because we are in a situation when to + prove as two we need to prove again s 2 so that's it that's a loop that + will never give us anything it means we applied the wrong rule so we need + to backtrack this step when we apply the rule left implication to s 2 we + erase is 4 in this 5 and try a different rule to apply to s 2 which rule + can apply to s 2 well as to is this it actually has implication on the + right so we can use the right implication rule and if we do that we get + a sequence s 6 which is this and this sequence immediately follows from + the identity axiom because it has promise are on the left and premise are + and goal are on the right and that is this axiom whatever other premises + and the premise X on the left premise X on the right and that is a type + variable so that's perfect we have done the proof as 6 follows from the + axiom and therefore we have proved s0 no more sequins need to be proved + and because sequence s0 shows this to be derived from no premises than + this formula is the theorem that's what the theorem means in the logic + so that is how we use this calculus to do proof search now we notice that + we were a bit stuck at some point we had a loop now if we are in the loop + we don't know what to do maybe we need to continue applying the same rule + maybe some new sequence come up or maybe we should not continue it is not + clear what to do and just looking at the rule left implication shows us + that it's copying this premise a implication B it is copied into the premises + of the new sequence and so it will generate a loop assuredly after the + second time you apply it however this sequence might be new so we might + need to apply it second time we don't know that so that is a problem it + will do now there have been a lot of work trying to fix this problem and + literally decades from research by mathematicians the main ones I found + were what are the off we published in the Soviet Union who de Meyer and + dick Hoff who published in the United States over this time discovered + gradually a new set of rules which is called ljt or the calculus ljt which + cures this problem of looping the way it clears this problem is by replacing + this rule left implication through four new rules which are listed here + all other rules are kept the same from this calculus except the rule left + implication which is replaced in what way so left implication was applying + it applied to a sequence when the sequin had an implication among the premises + or on the left to the left of the turnstile the new rules look in more + detail at what is that implication so that implication could have one of + the four expressions as the argument of the implication it could have an + atomic expression as the argument it would have a conjunction as the argument + could have a disjunction as the argument or it could have an implication + as the argument in our logic there are no more expressions except these + four atomic variables conjunctions disjunction and implications and so + we have here enumerated all the possibilities for what could be to the + left of the implication in this premise which I have here shown in the + blue in blue and so for each of these we do certain things replacing this + sequence with one or more other sequence again it's quite a lot of work + to prove that these rules are equivalent to these and also that the new + rules are somehow better they are not giving loops a lot of work which + I am NOT going to go through because that's far too complicated for the + scope so what we need suffice it to say that we have very smart people + who published on this and it is reasonably sure that this is correct so + the T in the name lgt starts stands for terminating so if we use these + rules in the same way by by creating a proof tree the proof tree will have + no loops and will terminate after a finite number of steps and there is + actually this paper that is also helpful for understanding how to implement + this algorithm and this paper shows explicitly how to construct an integer + function from sequence to integers which is a measure of the complexity + of the sequence and this measure decreases every time you apply a rule + so it strictly decreases and since this is a strictly decreasing measure + on the proof tree it means that all the next nodes in the proof tree will + have a smaller value of this measure so eventually it will hit zero and + the proof tree will terminate at that leaf either that or you have no more + rules to apply and if you have no more laws to apply then again mathematicians + have proved it means our sequence cannot be proved so this is an important + result that we are going to use and note that this this rule is quite complicat +ed it does a very interesting thing it takes this expression which has implicati +on inside an implication and it transforms this expression in a weird way + namely the B here is separated from the C by parenthesis but here it is + not separated so this transformation is highly non-trivial and unexpected + and its validity is based on this theorem that this in the intuitionistic + logic is equivalent to this equivalent means they're both following from + the other so from this promos that and from there follows this so this + key theorem was attributed to rob you off my dick off in this paper and + this is this lemma 2 which says that if this sorry that the this derivation + is if and only if that derivations will have these two equivalences and + the proof is trivial and the 34 is a reference to to borrow be off now + when a mathematician says that something is trivial doesn't mean that a + statement is easy to understand it doesn't mean that the proof is easy + to find or that it has trees easy to understand it means none of these + things it just means that right now for this mathematician it is not interestin +g to talk about how it is done that's all it means could be for any number + of reasons for example mathematicians could just be lazy or have no time + to again explain this and so they say it's trivial don't be don't be deceived + when you see somebody says that something is trivial in a mathematical + text so to prove this one stepping stone could be to prove this first this + is an easier theorem and if you prove this then clearly from here you can + get B to C B to C you can substitute in here you can get a to B and then + you have here a to B so in this way you can show this equivalence in one + direction now the proof of this statement is obviously trivial in order + to show the expression of this type I will use my short notation so this + is F which has this type the first argument of the function the second + is B which is at this type then we need to produce a see how do we produce + a C we apply F to an argument of this type the argument of this type is + a function that takes a and returns a B so we take some X of type a and + we return a B which was this B so we ignore this X we just returned that + B and that's the argument of F so this expression is the proof of this + sequence in other words this is the code that has this type and therefore + the proof must be available somehow so the details of proving this theorem + are left as an exercise for the reader again when you see in a mathematical + text that something is left as an exercise for the reader it does not mean + that it is easy to do it does not mean that for you it would be a useful + exercise to do it also does not mean that the author knows how to do it + it means none of these things it just means the author doesn't feel like + doing it right now and showing it to you for whatever reason could be because + they are lazy it could be because I don't know how to do it could be because + they feel that they should know how to do it but they don't really do know + how to do it could be any of these reasons don't be deceived when you see + something like this but of course I had to actually produce an expression + function of this type in order to implement my curry forward language because + as I will show in a moment we need to be able to implement all these has + code in order to help approver so why is that we believe the mathematicians + that the new rules are equivalent to the old rules which means that if + you find a proof using these rules somehow you should be able to find the + proof also using our initial rules which means that if you found that proof + it would easily translate that to code because each step here is directly + corresponding to a certain code expression as we have seen at the beginning + of this tutorial these cold expressions from each of these operations so + in order to do this with new rules in other words in order to create code + from proof using new rules we need to show equivalence or we need to show + how to get code out of each of the new rules now proof of a sequence means + that we have some expression let's say T what uses variables a B and C + of these types and expression itself has type G and also as I have shown + this could be conveniently seen as a function the T as a function from + a B and C from these three arguments to the type G so for each sequencing + a proof we should be able to show either that it follows from an axiom + one of these or that it show it follows from a derivation rule and the + derivations all transforms one proof into another the axioms are just fixed + expressions as we had before the axiom that actually didn't change between + our initial formulation of the logic and the new calculus lgt they actually + did not change the derivation rules changed each new derivation rule means + that you're given expressions that prove the sequence in the numerator + one or more and you are out of these expressions somehow you have to construct + an expression that proves this sequence now when I say an expression proves + the sequence what it means is that expression has the type that is described + by the sequence it's the same thing because we described types of expressions + through sequence and only those sequence that correspond to valid and existing + expressions in the programming language only those sequence can be proved + by the logic this is by construction so now we need to just find what are + these expressions that corresponds to each of the derivation rules in each + rule has a proof transformer function as I call it and the proof transfer + function is explicitly a function that takes one or more expressions that + are in the numerator and converts that to the expression in the denominator + that has this type so it has an expression as it has an explicit function + we need to write down for each of the derivation rules so let's see how + this is done for these two examples of derivation laws first example have + a rule that says if you want to derive this sequence we need to derive + these two sequence now this sequence represents an expression of type C + which uses an expression of type A plus B so let's represent this as a + function from a plus B to C now we will be able to just ignore these other + premises which are common arguments and all these functions we just pass + them and we don't write them out what is the proof transformer for this + derivation rule the proof transformer for it is a function that has two + arguments t1 which is the proof of this must be a function of type II to + see and t2 which is a proof of this sequence which must be a function of + type B to see now earlier I said that sequence represent expressions that + use certain variables but equivalently we can say these are functions that + take these variables and return these expressions that's more convenient + when you implement this in code so what we need is a function that takes + a to C and B to C and returns a function from a plus B to C and this is + the code that does it we take an argument of type a plus B and we return + a match expression if it's in the left we applied t1 to that value and + we get to see if it's in the right we apply t2 to that value and we get + a C so in any case we get a syllabus so this is a function from a plus + B to C as required another example is the proof transformer for this rule + this rule has one sequence going to one sequence so in order to transform + is proof into this we need a function that takes argument of type A to + B to C to D and returns a function of type tuple a B going to C to D so + here's the code we take a function f of type A to B to C to D we return + a function that takes a G of this type shown here in blue and return we + need to return a D so how do we get a deal we apply F to a function of + type A to B to C so we create that function out of G X of type a going + to Y of type B going to G of x1 so this is a function of type A to B to + C which is the argument of F as required and the result is of type D so + that is what we write so this kind of code is the proof transformer for + this derivation arrow and we need to produce this proof transformers for + every rule of the calculus lgt and I have done it because I have implemented + the Korea Howard library that uses LG T so I'll must have done it for each + flow this is a bit tedious because there are many of those rules and you + need to implement all this machinery of passing arguments no matter how + many in this gamma which are emitted from this notation for brevity but + in of course in the real code you have to deal with all that too so let's + see how this works on an example because once the proof tree is found we + need to start backwards from the leaves of the tree back to the root on + each step we take the proof expression apply the proof transformer to ative + according to the rule that was used on that step we get a new proof expression + and so on so for each sequence we will get a proof expression and at the + end we'll have a proof expression for the root sequence and that will be + the answer so I will denote denote by T I the proof expressions for the + sequence s hi so starting from s6 s6 was this sequence in our proof so + I mean yes just just going through the proof example it was here backwards + from a 6 back to a 0 s-six was this it followed from axiom identity it's + proof expression t6 is a function of two variables these two variables + of these two types and this function just returns the second variable so + it's a function of RR q and r and just denote this by our argued and Garibaldi' +s types r RQ variable of this type is hard here so this function is very + simple just ignores the first argument and returns or so that is what the + axiom does the next sequence was as to as to was obtained by rule our implicati +on or right implication from s 6 so the proof transformer for right implication + let's look at the right implication and see what the proof transformer + must be so we are given this sequence for this expression which is the + function body the function body that uses a variable of type a somehow + out of this we need to produce a function expression that takes an argument + of type a and returns that functional body so this is the code which is + just writing a new argument returning the function body that was our proof + transformer we need to convert function body into a function so we just + write that argument and arrow in the function body so in our case we need + this as a function body and so our t2 is a function of our Q and this function + is this the sequence s 3 followed from the axiom and so it was just this + function this is just the identity function then we used the left implication + so this was actually still done in the calculus algae but the same thing + works in the calculus lgt I'm just using algae because it's simpler for + example here proof transformer for the left implication is a little more + complicated and so if you look at it what what does it have to be it takes + these two expressions and returns this expression so it takes a function + from A to B to a and from B to C and it returns a function from A to B + to see how does it do it given a function a to b you use this to derive + a from it then you substitute that a into the function into B you get a + B when you use this to derive see from that B and that's your C so you + use this function a to be twice you put it in here once and then you get + an A and substitute back into the same function when you get a B then you + use that and that's exactly what the proof transformer does it takes this + rrq and it uses it twice substitutes into it something that was obtained + from one of the terms and then uses the second term on the result so then + this is the proof transformer for the rule left implication the result + of the proof transformation is the proof for the sequence s1 finally we + use the right implication again which is just this function construction + and we get the proof expression for the sequence s0 now this proof expression + is written through these t1 t2 t3 we have to substitute all this back in + order to get the final expression so if we substitute first of all we find + this is our our cubone going to tea one of our cutie one of our queue is + this so we have to put it here now t3 is just identity so we can just remove + that so that gets you riq going to our Q of T 2 T 2 is less if I have to + put it in T 6 is just identity on R so this is our going to our and so + finally you have this expression so that is the final code that has the + required type notice that we have derived this code completely algorithmic + to it there was no guessing we found which rules applied to the sequence + with transformed sequence according to the rules once we found the proof + which was if we use the calculus ljt the proof will be just a finite tree + with no loops it will terminate you can get an exhaustive depth-first search + for it for example and you find all the possible proofs if you want as + well well you will find many in any case in some for some expressions and + then we use the proof transformers which are fixed functions that you can + upfront compute for each these expressions are proof transformers applied + to the previous proofs so these are completely fixed algorithmically fixed + so we have derived this code completely algorithmically given this expression + this type so it is in this way that the career Howard correspondence allows + us to derive the code of functions from there type signatures another important + application of the correspondence is to analyze type by some morphisms + or type equivalences and I was led to this by asking the question so in + this logic or in the types are these operations plus and times as I denoted + them more like logic more like the disjunction and conjunction or are they + more like arithmetic plus and times because this is kind of not so clear + right away our logic is this intuitionistic logic it in any case this is + different from boolean logic so what are the properties of these types + really so are the properties such that it is better to think about these + operations as plus and times rather than logical conjunction and disjunction + can answer this question I looked at identities that we have in the water + these are some identities from simple ones obvious ones to less obvious + identities like this the equal sign here stands for implication in both + directions so both this implies that and vice versa because of this each + of the implications means a function so since these are all identities + in logic it means that for example the implication from here to here is + a theorem of logic and so it can be implemented as we know all our identities + in logic can be implemented in code and we even have an algorithm now that + can automatically produce proofs and automatically produce code so that + means for any of these identities that has some ik some expression X on + the left and some Y on the right so some kind of X equals y we have X implies + Y and y implies X if we convert that to code we will have a pair of functions + function from X to one and the function from Y to X what do these functions + do well they convert values in some ways from type X to type Y and back + so do these functions Express the equivalence of the types x and y so that + any value of type X can be converted to some equivalent value type while + and back without any loss of information is that so that was the question + I asked I looked at some examples well first what does it mean more rigorously + that types are equivalent for as mathematicians say isomorphic the types + are isomorphic and we will use this notation for that if there is a one-to-one + correspondence between the sets of values of these types and in order to + demonstrate that we need a pair of functions one going from A to B the + other going from B to a such that the composition of these functions in + both directions is equal to identity function so F compose G or F value + G will give you from A to B and then from B to a is back so that would + be identity of a to a this will be identity of B to B if this is true if + the composition is identity it means we indeed did not lose any information + let's consider an example this is an identity in the logic a conjunction + with one is equal to a in Scala the types responding to the left and the + right hand sides of this conjunction all of this are equivalent are the + conjunction of a and unit and a itself now we need functions with these + types indeed we can write functions is having these types a pair of a and + unit we need to produce an a out of that we'll just take the first element + of the pair you are done take an X of type a will produce tuple of a and + unit very easy just put a unit value in the tuple in here done and it's + easy to verify that composition of these functions will not change any + values so it will be identity in both directions another example this is + an identity in logic if this is understood as a disjunction one or a or + true or a is true that is an identity in logic for theorem in the logic + are the types equivalent though the type for 1 plus a is the option in + Scala it is option in Haskell at is called maybe this type is standard + library type in pretty much every functional programming language now option + of a is a disjunction of one or unit and a it is certainly not equivalent + to just unit because this type could contain a value of a in it but this + could not so there is no way that you could transform this type to this + and then back without losing information you could transform so since this + is a theorem you have functions from this type to this type and back some + functions you have them but these functions do not compose to identity + they cannot because what if you had a here you must map it into unit from + this unit back you must map into this unit you cannot get an a out of unit + and so that will erase this information and that cannot become isomorphism + so we see that some logic identities do yield isomorphism types but others + do not why is that let's look at some more examples to figure out why in + all these examples we can implement functions F 1 and F 2 between the two + sets to two types in both directions and then we can check we certainly + can implement them because these are logical identities but then we can + check if the compositions are identity functions and if so the types are + isomorphic but we find that in the first three examples we can do it but + in this last example we can note now I have written the logical identities + logical theorems with the arithmetic notation I call this arithmetical + notation because this suggests arithmetic operations plus and times and + if you look at these identities this looks like a well-known algebraic + identity from the school algebra in this too but this certainly seen your + own as an arithmetic as an as an arithmetic identity this is certainly + not true in arithmetic it is true in logical if you replace this with disjuncti +on and this with conjunction this is an identity in logic so this suggests + an interesting thing if you replace disjunction by plus and conjunction + by x and the result is an identity in arithmetic then it is an isomorphism + of types otherwise it is not let's see why this is so indeed this is so + I call this the arithmetic arithmetic oh very hard correspondence to see + how it works let's consider only the types without loss of generation of + generality that have a finite set of possible values for example a boolean + type has only two possible true and false integer let's say in the computers + all the integers are fine nights ago so those types have a finite set of + possible values and this does not limit our generality because in the computer + everything is finite all types have a finite set of possible values now + let's consider how many values a given type has so that would be the size + of the type or using the mathematical terminology it's called a cardinality + of the type so let's see what is the cardinality of various type constructions + the sum type for example if the cardinality of types a and B is known and + the cardinality of a plus B the sum type the disjunction of a and B is + the sum of the two cardinalities or sizes this is because a value of the + disjunction type is constructed as either a value of the first part or + a value of the second part and so you cannot have both together and so + obviously the different number of values is just the sum of the two sizes + that the number of different values of the sum type is just the sum of + the numbers of different values of types a and B for the product type again + we have an interesting thing it's the arithmetic product of the sizes of + a and B because for every a value you could have an arbitrary B value so + this is a direct product or transient product of sets and we have school + level identities about the operations plus and times such as these identities + or these all of these identities are valid for arithmetic and they show + if you translate that into statements about the sizes of types they show + that the size of the type on the left is equal to the size of the type + on the right and that is very suggestive in other words if you take a identity + like this and you compute the size of the type on the left and the size + of the type on the right you get an arithmetic identity of the sizes but + you don't get that identity here because the earth medical formula is not + right this is very suggestive if the sizes are equal and maybe the types + are equivalent or isomorphic when the sizes are not equal then certainly + they cannot be equivalent the function type very interestingly also is + described in the same way it provides the set of all maps between the two + sets of values so for example from integer to boolean that would be all + the functions that take some integer and return some boolean so that's + and a number of boolean values ^ the number of integer values that's how + many different functions you can have as a combinatorial number so it's + an exponential and so the size of the type of function a to be is the size + of the type of B ^ the size of type of a and again we have all the school + identities about powers and how to multiply powers and so on and they are + directly translated into these three identities if you take the sizes of + the types on the left and on the right the sizes will be equal due to these + three identities since the sizes are equal it's very likely that the type + our actual equivalent so far haven't seen any counter examples to this + in these constructions so this gives us a meaning of the Curie Howard correspon +dence so far we have seen three facets of the curly Howard correspondence + one is the correspondence between types and logical formulas two is the + correspondence between code and proofs and three the correspondence between + the cardinality of a type or the set size of the type and the arithmetic + identities that we have in the school algebra about these types so arithmetical + identities signify type equivalence or isomorphism while logic identities + only talk about how you create some value of this type out of value of + another type so that does not guarantee that it preserves information it + just guarantees that you can implement some function of that type it doesn't + tell you that the function will be an isomorphism so if one type is logically + equivalent to another it means are equally implementable if one is implementabl +e another is also implementable but no more than that whereas arithmetical + identities actually tell you about isomorphism of types therefore if you + look at types and write them using my preferred notation which is using + the arithmetic all symbols instead of logical symbols instead of these + I'll use these symbols if I do that this is very suggestive of a possible + isomorphism of types then it becomes very easy for me to reason about types + I can see right away that these two are isomorphic types or that these + two are isomorphic types because I am used to looking at school algebra + it's very obvious then that this is not an isomorphism of types because + this doesn't make sense in the school algebra so reasoning about isomorphic + types is basically school level algebra involving polynomials and powers + so if you are familiar with all these identities as you should be it will + be very easy for you the reason about what types are equivalent as long + as all these types are made up of constants or primitive types disjunctions + tuples or conjunctions and functions which will then directly be translated + into exponential polynomial expressions constants sums products and expand + powers or Exponential's so I call these exponential polynomial types that + is types built up from these type constructions so all we have been talking + about in this tutorial is what I call exponential polynomial types these + are the basic type constructions that I started with tuple product function + exponential disjunction some unit constant or 1 now just one comment that + in the functional programming community today there is a terminology algebraic + types so people usually call algebraic types the types that are made from + constant types sums and products excluding Exponential's I do not find + this terminology it's very helpful I find it confusing because what is + particularly an algebraic about these identities these are identities of + school algebra the properties of the function type are described by algebraic + identities like this so it would be strange to call the function type not + algebraic whereas these types are algebraic they are very similar to each + other in terms of their properties being described by identity is known + from school algebra so instead of algebraic types I would prefer to say + polynomial types this is much more descriptive and precise and if you want + to talk about function types as well then you just can you can just say + exponential polynomial types or exfoli types for short so by way of summarizing + what we have done so far what are the practical implications of the career + Howard correspondence so one set of implications is actually for writing + code and reason and eternal code one thing we can do now is if we're given + a function with some type and usually this will be typed with type parameters + all type trainers fully parametric types such as the function we have been + considering here all these functions do not have any types that are specific + like integer or string all the types are fully parametric and then there + are some constructions some type expressions made out of these types so + these are what I call fully parametric functions for these functions we + have a decision procedure an algorithm that based on the ljt calculus which + decides whether this function can be implemented in code and computer scientist +s a type is inhabited if you can produce a value of this type in your program + so CH of T is this proposition which they call type is inhabited and I + prefer to call it just that you can compute a value of this type or code + has the type O code can create a value of this type and so we have a algorithm + that can also generate the code from type when it is possible if it is + not possible the algorithm will tell you so often not always but often + this algorithm can be used actually to generate the code you want we can + also use what I call the arithmetic of glory Harvard correspondence to + reason about type isomorphisms and to transform types isomorphic we simplify + type expressions just like we simplify expressions in school level algebra + by expanding brackets by permuting the order of terms like a plus B is + equal to B plus a or associativity a times B all times C can be expanded + and so on so this allows us once we have written types in the short notation + in the notation that I prefer which resembles school algebra because it + uses the plus and times symbols instead of the logic symbols so once we + rewrite our types and this notation which I have been doing consistently + in this tutorial it enables us the reason very easily but which types are + equal or isomorphic because we are all familiar with the school level algebra + what are the problems that we cannot solve using this knowledge one thing + we cannot do is to generate code automatically such that it will be an + isomorphism so for instance in an example here we are able to generate + automatically the code of these functions but it will not be an isomorphism + and the lgt algorithm cannot check that this is nice a morphism that's + the important thing this algorithm does not know about equations or isomorphism +s it only knows that it found some code that has the type you wanted whether + this code is useful to you or not we don't know the algorithm doesn't know + this also if the algorithm finds several such several proofs of a sequence + it will generate several not in equivalent versions of your code it doesn't + know which one is is useful maybe some of them are useless maybe not the + algorithm cannot automatically decide that in general another thing we + cannot do is to express complicated conditions via types such as that array + is sorted the type system is not powerful enough in all the languages I + listed you need a much more powerful type system such as that in the programmin +g language interests or add them or cook those are much more powerful type + systems that can express such complicated conditions but for those type + systems there is no algorithm that will generate code another thing we + cannot do is to generate code that has type constructors such as the map + function here's an example in Scala this is a map function on a list so + there's the list of a a is a type parameter and then we say dot map and + map has another type frame to be it takes a function from A to B for any + B so a is fixed but now from any B we can take a function from A to B and + generate a list of B so if we wrote this formula in the short type notation + this would look something like this I'm writing subscript a because this + is a type parameter so this is like an argument or a type parameter I'm + writing it like this and then from this this is the first argument of the + function and then there is a second argument which is this F and that is + another quantifier for B inside parentheses so this formula has a quantifier + inside so far we have been dealing with formulas that have all quantifiers + outside and so we never write quantifiers explicitly but here we have to + write them inside this is a more powerful logic which is called first-order + logic in other words this is a logic where you have quantifiers anywhere + in the formula including inside the formula unfortunately this logic is + undecidable so there is no algorithm that we can use either to find the + proof and therefore code freedom type or to show that there is no proof + no code so we're kind of stuck in all these directions some more remarks + about the curry Harvard correspondence first is that only with parameterize + types we can get some interesting information out of it if we take concrete + types like integer then the proposition CH event meaning that our code + can have a value of type int it that's always true can always write any + some integer value we don't need any previous data for it so for all specific + types all these propositions are always choice completely void of information + the only interesting part comes when we start considering type variables + if we start asking can we make a type which is either of a B going to a + going to B in soon for all a B once we start doing this with type parameters + a B and so on then we get interesting information as we have seen in this + tutorial another remark is that functions like this one are not sufficiently + described by their type so that this is the type of integer going to integer + now looking at this type we can put this into a sequence but we'll never + get enough information to actually get this function so only certain class + of functions which are fully typed biometric their type signature is informativ +e enough so that we can derive code automatically only in much more powerful + type systems you can have type information that is enough to specify fully + a code like this another caveat is that I don't know the proof that arithmetic + identity guarantees the type equivalence it is certainly a necessary condition + because if two types have different cardinality or different size of their + sets of values that they cannot be equivalent or they cannot be isomorphic + so this is a necessary condition but it's not a sufficient condition it + looks like I don't know if this is sufficient I haven't seen any counter + examples so far final remarks about type correspondence the logical constant + false did not appear in any of my slides so far this was on purpose it + has extremely limited practical use in programming languages because actually + we have types corresponding to false Scala has type called nothing Haskell + has type usually called void that corresponds to the logical constant false + what does it mean CH of nothing is false it means your code can never have + a value of type nothing or in Haskell void you can never compute a value + of this type so clearly it has a very limited practical significance you + will never be able to compute any values of this type ever in any program + it's identically falseness this constant so if you want to add it to the + logic it's very easy you just have one rule and you're not done you can + derive things with it if you want but they will have almost never any use + in practical code also we did not talk about negation none of the calculus + calculate that I should have in logical negation as in operation again + for the same reason we do not have a programming language construction + that represents logical negation negation by definition is like this is + an application from 8 to 4 so that's not a not a means from a follows falsehood + now since you cannot ever get false in a programming language you cannot + really implement this function in any useful sense and so i have seen some + haskell library that used this type void as a type parameter in some way + but certainly it's a very limited and rare use and so it is not really + lumen 18 to include negation it could probably find some very esoteric + uses of it but almost never useful and finally there is another set of + important implications from the Kurihara correspondence these are implications + for people who want to design new programming languages as we have seen + the Karaka with correspondence maps the type system of a programming language + into a certain logical system where prepositions follow from each other + or can be proved from each other and this enables us to reason about programmed + to see what kind of code can be written if some other kind of code can + be written and logical reasoning is very powerful it's simpler than trying + to write code and it gives you algorithms and all kinds of mathematical + results that have been found over the centuries so languages like those + listed here have all the five type constructions that I wasted in the beginning + of this tutorial and mapping them into logic gives a full constructive + logic or full intuitionistic logic with all logical operations and or so + conjunction disjunction implication and the truth constant whereas languages + such as C C++ Java and c-sharp and so on they're mapped to incomplete logics + because they do not have some of these operations for instance they do + not have type constructions of correspond to disjunction we also do not + have the true constant or the false constant so they are mapped to a logic + that lacks some of the foundational logical operation so it can be only + fewer theorems can be proved in that logic and so your reasoning about + theory types is hampered languages called scripting languages sometimes + such as Python or JavaScript will be and so on also our belongs there in + that line those languages only have one type they actually don't check + types at compile time and so they're mapped to logics with only one proposition + those logics are extremely small in terms of what kind of things you can + reason about and so if you write a program in these languages you are completel +y unable to reason at the level of types whereas in these languages you + are able to reason but in a limited way you're not having a complete logic + so this suggests a principle for designing the type system in a new programming + language the first step would be to choose a good and complete logic that + is free of inconsistency mathematicians have studied all kinds of logics + and they are always interested in questions such as is this logic consistent + consistent means you cannot derive false from true is this logic complete + can you derive all things that are true are there enough axioms and rules + of derivation or maybe there are too many axioms and rules of derivation + you can delete some of them and have fewer mathematicians have always been + interested in such questions they found all kinds of interesting logics + where you can derive a lot of interesting theorems non trivial theorems + and they found the minimum sets of axioms and rules of derivations for + these logics use their results take one of the logics that they do them + and develop such as intuitionistic logic model logic temporal logic linear + logic and so on take one of these logics for each of the basic operations + of this logic provide type constructions in your programming language that + are easy to use for instance your logic has disjunction implication or + something else provide a type constructor for each of them that's easy + to use easy to write down such as provided by the languages we have seen + then every type will be mapped to a logical form of the OPF logical formula + for every type and there will be a type for every logical formula and then + for each rule of the new logic for each derivation rule there should be + a construct in the code that corresponds to it so that you could transform + proofs in logic into code and code into proofs if you do that your language + will be faithful to the scorecard correspondence you will be able to use + logic to reason about your language and one important result at this level + while we have seen that you can sometimes generate code that is maybe nice + but a very important result is that if your logic is free of inconsistency + it means that no program will ever be able to derive an inconsistent an + inconsistent type means that you had a function that requires some type + a but it was called with a different type beam which is incompatible and + that basically crashes so in languages like C and C++ we have all kinds + of crashes like a segmentation fault in Java the exceptions nullpointerexceptio +n or class cast exception which happens when you call a function on the + wrong type of argument and that happens if your logic is inconsistent if + your logic can derive incorrect statements from correct premises then if + you translate that derivation into code and the that code will derive incompati +ble type at the wrong place and it will crash the crash will happen at runtime + the compiler will not catch this inconsistency because the compiler only + checks the logic of types and the logic checks out you have followed the + rules of derivation of the logic the compiler can check out all these logical + rules but the compiler does not know that your logic is inconsistent maybe + and then it will deep have derived an inconsistent result falsehood from + truth for example and that will crash at runtime now we know that crashing + at runtime is not a good outcome so in fact languages like Oh camel have + been studied and for other languages some subsets of Haskell I believe + called safe Haskell have been studied and it has been shown that they cannot + crash and they're the way to show it mathematically is to use the fact + that they are based on a complete and consistent logic and then all you + need to show is that your compiler does not have some critical bugs that + allow it to oversee that you have not followed the derivation rules of + the logic that is an extremely valuable feature of functional programming + languages that are based on the Curie habit correspondence you can prove + their safety at compile time or at least exclude a large number of possible + bugs and errors certainly these languages are quite large and they include + features that are not covered by the Carey Hart correspondence type constructor +s that I have not considered in this tutorial and those might may not be + safe but at least the foundation of these languages the foundation of the + type system will be safe so that is the final lesson from the great Howard + correspondence this concludes the tutorial \end_layout \end_inset - (see footnote -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "fn:Bornat-proof-book" -plural "false" -caps "false" -noprefix "false" - -\end_inset - - on page -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand pageref -reference "fn:Bornat-proof-book" -plural "false" -caps "false" -noprefix "false" - -\end_inset -). \end_layout \end_body diff --git a/sofp-src/sofp-curry-howard.tex b/sofp-src/sofp-curry-howard.tex index 2b1decf6b..6a78e26e5 100644 --- a/sofp-src/sofp-curry-howard.tex +++ b/sofp-src/sofp-curry-howard.tex @@ -23,118 +23,132 @@ \chapter{The logic of types. III. The Curry-Howard correspondence\label{chap:5-C \section{Values computed by fully parametric functions} -\subsection{Motivation} +\subsection{Motivation and outlook\label{subsec:Motivation-and-outlook}} Consider the following sketch of a fully parametric function\textsf{'}s Scala code: - -\begin{wrapfigure}{l}{0.54\columnwidth}% -\vspace{-0.65\baselineskip} \begin{lstlisting} def f[A, B, ...]: ... = { val x: Either[A, B] = ... // Some expression here. - ... } + ... +} \end{lstlisting} - -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -\noindent If this program compiles without type errors, it means that -the types match and, in particular, that the function \lstinline!f! -is able to compute a value \lstinline!x! of type \lstinline!Either[A, B]!. +If this program compiles without type errors, it means that the types +match and, in particular, that the function \lstinline!f! is able +to compute a value \lstinline!x! of type \lstinline!Either[A, B]!. It is sometimes \emph{impossible} to compute a value of a certain type in fully parametric code. For example, the fully parametric function -\lstinline!fmap! shown in Section~\ref{subsec:Disjunctive-Example-option-1} +\lstinline!fmap! shown in Example~\ref{subsec:Disjunctive-Example-option-1} cannot compute a value of type \lstinline!A!: - -\begin{wrapfigure}{l}{0.54\columnwidth}% -\vspace{-0.65\baselineskip} \begin{lstlisting} def fmap[A, B](f: A => B): Option[A] => Option[B] = { val x: A = ??? // Cannot compute x here! - ... } + ... +} \end{lstlisting} - -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -\noindent The reason is that no fully parametric code can compute -values of type \lstinline!A! from scratch without using a previously -given value of type \lstinline!A! and without applying a function -that returns values of type \lstinline!A!. In \lstinline!fmap!, -no values of type \lstinline!A! are given as arguments; the given -function \lstinline!f: A => B! returns values of type \lstinline!B! -and not \lstinline!A!. The code of \lstinline!fmap! must perform -pattern matching on a value of type \lstinline!Option[A]!: - -\begin{wrapfigure}{l}{0.54\columnwidth}% -\vspace{-0.65\baselineskip} +The reason is that no fully parametric code can compute values of +type \lstinline!A! \textsf{``}from scratch\textsf{''}, that is, without using any +previously given value of type \lstinline!A! and without applying +a previously given function that returns a value of type \lstinline!A!. +In \lstinline!fmap!, no values of type \lstinline!A! are given as +arguments; the given function \lstinline!f: A => B! returns values +of type \lstinline!B! and not \lstinline!A!. The code of \lstinline!fmap! +must perform pattern matching on an argument of type \lstinline!Option[A]!: \begin{lstlisting} -def fmap[A, B](f: A => B): Option[A] => Option[B] = { +def fmap[A, B](f: A => B)(pa: Option[A]): Option[B] = pa match { case None => val x: A = ??? // Cannot compute x here! ... case Some(a) => val x: A = a // Can compute x in this scope. - ... } + ... +} \end{lstlisting} - -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -\noindent Since the case \lstinline!None! has no values of type \lstinline!A!, +Since the case \lstinline!None! has no values of type \lstinline!A!, we are unable to compute a value \lstinline!x! in that scope (as long as \lstinline!fmap! remains a fully parametric function). -\textsf{``}Being able\textsf{''} to compute \lstinline!x:A! means that, if needed, +\textsf{``}Being able\textsf{''} to compute \lstinline!x: A! means that, if needed, the code should be able to \emph{return} \lstinline!x! as a result value. This requires computing \lstinline!x! in all cases, not just -within one part (\lstinline!case ...!) of a pattern-matching expression. +within one part (\lstinline!case ...!) of a pattern-matching expression. +In other words, we should be able to implement the following type +signature: +\begin{lstlisting} +def bad[A, B](f: A => B)(pa: Option[A]): A = ??? +\end{lstlisting} -The body of \lstinline!fmap! also cannot compute any values of type -\lstinline!B!. Since no arguments of type \lstinline!B! are given, -the only way of obtaining a value of type \lstinline!B! would be -to apply the function \lstinline!f: A => B! to \emph{some} value -of type \lstinline!A!. But we just saw that the body of \lstinline!fmap! -cannot compute any values of type \lstinline!A!. +So, the question \textsf{``}can we compute a value of type \lstinline!A! +within a fully parametric function with arguments of type \lstinline!B! +and \lstinline!C!\textsf{''} is equivalent to the question \textsf{``}can be implement +a fully parametric function of type \lstinline!(B, C) => A!\textsf{''}. From +now on, we will focus on the latter kind of questions. + +Here are some other examples where \emph{no} fully parametric code +can implement a given type signature: +\begin{lstlisting} +def bad2[A, B](f: A => B): A = ??? -Another example where one cannot compute a value of a certain type -is the following code: +def bad3[A, B, C](p: A => Either[B, C]): Either[A => B, A => C] = ??? +\end{lstlisting} -\begin{wrapfigure}{l}{0.54\columnwidth}% -\vspace{-0.1\baselineskip} +The problem with \lstinline!bad2! is that no data of type \lstinline!A! +is given, while the given function \lstinline!f! returns values of +type \lstinline!B!, not \lstinline!A!. + +The problem with \lstinline!bad3! is that it needs to hard-code the +decision of whether to return the \lstinline!Left! or the \lstinline!Right! +part of \lstinline!Either!. That decision cannot depend on the function +\lstinline!p! because one cannot pattern-match on a function, and +because \lstinline!bad3! does not receive any data of type \lstinline!A! +and so cannot call \lstinline!p!. Suppose \lstinline!bad3! is hard-coded +to always return a \lstinline!Left(f)! with some \lstinline!f: A => B!. +It is then necessary to compute \lstinline!f! from \lstinline!p!, +but that is impossible: the given function \lstinline!p! may return +either \lstinline!Left(b)! or \lstinline!Right(c)! for different +values of its argument (of type \lstinline!A!). This data is insufficient +to create a function of type \lstinline!A => B!. Similarly, \lstinline!bad3! +is not able to return \lstinline!Right(f)! with some \lstinline!f: A => C!. + +In all these examples, we see that the impossibility of implementing +a type signature means that the information given in a function\textsf{'}s +arguments is in some way insufficient for computing the result value. + +The type signature inverse to that of \lstinline!bad3! is: +\begin{lstlisting} +def good3[A, B, C](q: Either[A => B, A => C]): A => Either[B, C] = ??? +\end{lstlisting} +This type signature \emph{can} be implemented, but only in one way: \begin{lstlisting} -def before[A, B, C](f: A => B, g: B => C): A => C = { - // val h: C => A = ??? // Cannot compute h here! - a => g(f(a)) // Can compute a value of type A => C. +def good3[A, B, C](q: Either[A => B, A => C]): A => Either[B, C] = q match { + case Left(k) => { a => Left(k(a)) } + case Right(k) => { a => Right(k(a)) } } \end{lstlisting} -\vspace{-1.2\baselineskip} -\end{wrapfigure}% - -\noindent The body of \lstinline!before! may only use the arguments -\lstinline!f! and \lstinline!g!. We can compose \lstinline!f! and -\lstinline!g! to get a value of type \lstinline!A => C!. But it -is impossible to compute a value \lstinline!h! of type \lstinline!C => A!, -no matter what code we try to write. The reason is that the body of -\lstinline!before! has no given values of type \lstinline!A! and -no functions that return values of type \lstinline!A!, so a nameless -function such as \lstinline!{c:C => ???}! cannot compute a return -value of type \lstinline!A!. Since a fully parametric function cannot -create values of an arbitrary type \lstinline!A! from scratch, computing -\lstinline!h! within the body of \lstinline!before! seems to be -impossible. - -Can we prove rigorously that a value of type \lstinline!C => A! cannot -be computed within the body of \lstinline!before!? Or, perhaps, a -clever trick could produce a value of that type? So far, we only saw -informal arguments about whether values of certain types can be computed. -To make those arguments rigorous, we need to translate statements -such as \textsf{``}a fully parametric function \lstinline!before! can compute -a value of type \lstinline!C => A!\textsf{''} into mathematical formulas -with rules for proving them true or false. +So, when working with fully parametric code and looking at some type +signature of a function, we may ask the question \textemdash{} is +that type signature implementable, and if so, can we derive the code +by just \textsf{``}following the types\textsf{''}? It is remarkable that this question +can be asked at all. When working with non-functional programming +languages, the notion of fully parametric functions is usually not +relevant, and implementations cannot be derived from types. But in +functional programming, fully parametric functions are used often. +It is then important for the programmer to know whether a given fully +parametric type signature can be implemented, and if so, to be able +to derive the code. + +Can we prove rigorously that the functions \lstinline!bad!, \lstinline!bad2!, +\lstinline!bad3! cannot be implemented by any fully parametric code? +Or, perhaps, we were mistaken and a clever trick \emph{could} produce +some code for those type signatures? + +So far, we only saw informal arguments about whether values of certain +types can be computed. To make those arguments rigorous, we need to +translate statements such as \textsf{``}a fully parametric function \lstinline!before! +can compute a value of type \lstinline!C => A!\textsf{''} into mathematical +formulas with rules for proving them true or false. The first step towards a rigorous mathematical formulation is to choose a precise notation. In Section~\ref{subsec:Disjunctions-and-conjunctions}, @@ -149,7 +163,7 @@ \subsection{Motivation} \end{align} Here $X$, $Y$, ..., $Z$, $A$ may be either type parameters or more complicated type expressions, such as $B\rightarrow C$ or $(C\rightarrow D)\rightarrow E$, -built from other type parameters. +built from some type parameters. If values of types $X$, $Y$, ..., $Z$ are given, it means we \textsf{``}already have\textsf{''} values of those types, i.e., the propositions ${\cal CH}(X)$, @@ -166,50 +180,67 @@ \subsection{Motivation} the proposition ${\cal CH}(A)$ is called the \textbf{goal}\index{sequent (in logic)!goal} of the sequent. -Sequents provide a notation for the questions about what types of -values may be computed by fully parametric functions. Since our goal -is to answer such questions rigorously, we will need to be able to -\emph{prove} sequents of the form~(\ref{eq:ch-example-sequent}). -The following sequents correspond to some examples we just saw: +Sequents provide a notation for the questions about types of fully +parametric functions. Since our goal is to answer such questions rigorously, +we will need to be able to \emph{prove} sequents of the form~(\ref{eq:ch-example-sequent}). +The following sequents correspond to the type signatures we just saw: \begin{align*} {\color{greenunder}\text{\texttt{fmap} for \texttt{Option}}:}\quad & {\cal CH}(\text{\texttt{A => B}})\vdash{\cal CH}(\text{\texttt{Option[A] => Option[B]}})\\ {\color{greenunder}\text{the function \texttt{before}}:}\quad & {\cal CH}(\text{\texttt{A => B}}),{\cal CH}(\text{\texttt{B => C}})\vdash{\cal CH}(\text{\texttt{A => C}})\\ -{\color{greenunder}\text{value of type }A\text{ within \texttt{fmap}}:}\quad & {\cal CH}(\text{\texttt{A => B}}),{\cal CH}(\text{\texttt{Option[A]}})\vdash{\cal CH}(\text{\texttt{A}})\\ -{\color{greenunder}\text{value of type }C\rightarrow A\text{ within \texttt{before}}:}\quad & {\cal CH}(\text{\texttt{A => B}}),{\cal CH}(\text{\texttt{B => C}})\vdash{\cal CH}(\text{\texttt{C => A}}) +{\color{greenunder}\text{the function }\text{\texttt{bad}}:}\quad & {\cal CH}(\text{\texttt{A => B}}),{\cal CH}(\text{\texttt{Option[A]}})\vdash{\cal CH}(\text{\texttt{A}})\\ +{\color{greenunder}\text{the function }\text{\texttt{bad2}}:}\quad & {\cal CH}(\text{\texttt{A => B}}),{\cal CH}(\text{\texttt{B => C}})\vdash{\cal CH}(\text{\texttt{A}}) \end{align*} -So far, we only gave informal arguments towards proving the first -two sequents and \emph{disproving} the last two. We will now develop -tools for proving sequents rigorously. We will then be able to prove -that a given fully parametric function can compute values of a certain -type (or that it cannot). +So far, we only saw informal arguments towards proving the first two +sequents and disproving the last two. We will now develop tools for +proving sequents rigorously. In formal logic, sequents are proved\index{proof (in logic)} by starting with certain axioms and following certain derivation rules. Different choices of axioms and derivation rules will give different \emph{logics}. We will need to discover the correct logic for reasoning about sequents -with ${\cal CH}$-propositions. That logic\textsf{'}s axioms and derivation -rules will give correct answers about implementable and non-implementable -types. +with ${\cal CH}$-propositions. To discover that logic\textsf{'}s complete +set of axioms and derivation rules, we need to examine systematically +what types and code constructions are possible in a fully parametric +function. The resulting logic is known under the name \textsf{``}constructive +logic\textsf{''}. That logic\textsf{'}s axioms and derivation rules directly correspond +to programming language constructions allowed by fully parametric +code. For that reason, constructive logic gives correct answers about +implementable and non-implementable type signatures of fully parametric +functions. + +We will then be able to borrow the results and methods already available +in the mathematical literature on formal logic. The main result is +an algorithm (called LJT) for finding a proof for a given sequent +in the constructive logic. If a proof is found, the algorithm also +provides the code of a function that has the type signature corresponding +to the sequent. If a proof is not found, it means that the given type +signature cannot be implemented by fully parametric code. \subsection{Type notation and ${\cal CH}$-propositions for standard type constructions\label{subsec:Type-notation-and-standard-type-constructions}} A proposition ${\cal CH}(A)$ may be true with one set of premises such as ${\cal CH}(X)$, ${\cal CH}(Y)$, ..., ${\cal CH}(Z)$ but false with another. Here and in the following sections, we will be -reasoning about ${\cal CH}$-propositions within the body of a \emph{chosen} -fully parametric function, i.e., with a fixed set of premises. We -will then temporarily omit the premises and write just ${\cal CH}(A)$ -instead of ${\cal CH}(X),{\cal CH}(Y),...,{\cal CH}(Z)\vdash{\cal CH}(A)$. -In later sections, we will need to write full sets of premises for -sequents. +reasoning about ${\cal CH}$-propositions within sequents of the form: +\[ +{\cal CH}(X),{\cal CH}(Y),...,{\cal CH}(Z)\vdash{\cal CH}(A) +\] +that represent type signatures of fully parametric functions. It will +be convenient to shorten the notation and to denote all premises by +the symbol $\Gamma$. We will then write just $\Gamma\vdash{\cal CH}(A)$ +instead of ${\cal CH}(X),{\cal CH}(Y),...,{\cal CH}(Z)\vdash{\cal CH}(A)$. In Section~\ref{subsec:Disjunctions-and-conjunctions} we saw examples of reasoning about ${\cal CH}$-propositions for case classes and for disjunctive types. We will now extend this reasoning systematically to all type constructions that fully parametric programs could use. -A special type notation\index{type notation} explained in this section -will help us write type expressions more concisely. (See Appendix~\ref{chap:Appendix-Notations} -for a summary of our type notation.) +The result will be that we can rewrite ${\cal CH}$-propositions with +arbitrary type expressions, such as ${\cal CH}($\lstinline!Either[(A, A), Option[B] => Either[(A, B), C]]!$)$, +in terms of ${\cal CH}$-propositions for simple type parameters: +${\cal CH}(A)$, ${\cal CH}(B)$, etc. A special type notation\index{type notation} +explained in this section will help us write type expressions more +concisely. (See Appendix~\ref{chap:Appendix-Notations} for a summary +of our type notation.) There exist \textbf{six} \textbf{standard type constructions}\index{six type constructions} supported by all functional languages: primitive types (including @@ -242,19 +273,12 @@ \subsection{Type notation and ${\cal CH}$-propositions for standard type constru \end{lstlisting} defines a named unit type. We can compute a value of type \lstinline!N1! without using any other given values: - -\begin{wrapfigure}{l}{0.42\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} val x: N1 = N1() \end{lstlisting} - -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -\noindent So, the proposition ${\cal CH}($\lstinline!N1!$)$ is -always true. Named unit types are also denoted by $\bbnum 1$, just -as the \lstinline!Unit! type itself. +So, the proposition ${\cal CH}($\lstinline!N1!$)$ is always true. +Named unit types are also denoted by $\bbnum 1$, just as the \lstinline!Unit! +type itself. \paragraph{1b) Rule for the void type} @@ -272,6 +296,7 @@ \subsection{Type notation and ${\cal CH}$-propositions for standard type constru ... val x: String = "abc" // We can always compute a `String` value. ... +} \end{lstlisting} So, the rule for primitive types is the same as that for the \lstinline!Unit! type. For example, $\mathcal{CH}(\text{String})=True$. @@ -316,9 +341,9 @@ \subsection{Type notation and ${\cal CH}$-propositions for standard type constru \[ \text{RootsOfQ}\triangleq\bbnum 1+\text{Double}+\text{Double}\times\text{Double}\quad. \] -Here, the type notation is significantly shorter because it omits -all case class names and part names from the type definitions. Using -the type notation, the rule for disjunctive types is written as: +Here the type notation is significantly shorter because it omits all +case class names and part names from the type definitions. Using the +type notation, the rule for disjunctive types is written as: \[ {\cal CH}\left(A+B+...+C\right)={\cal CH}(A)\vee{\cal CH}(B)\vee...\vee{\cal CH}(C)\quad. \] @@ -329,27 +354,20 @@ \subsection{Type notation and ${\cal CH}$-propositions for standard type constru Consider now a function type such as \lstinline!A => B!. This type is written in the type notation as $A\rightarrow B$. To compute a value of that type, we need to write code like this: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.65\baselineskip} \begin{lstlisting} val f: A => B = { (a: A) => ??? // Compute a value of type B in this scope. } \end{lstlisting} - -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -\noindent The inner scope of the function needs to compute a value -of type $B$, and the given value \lstinline!a:A! may be used for -that. So, ${\cal CH}(A\rightarrow B)$ is true if and only if we are -able to compute a value of type $B$ when we are given a value of -type $A$. To translate this statement into the language of logical -propositions, we need to use the logical\index{logical implication} -\textbf{implication}, ${\cal CH}(A)\Rightarrow{\cal CH}(B)$, which -means that ${\cal CH}(B)$ can be proved if we already have a proof -of ${\cal CH}(A)$. So, the rule for function types is: +The inner scope of this function needs to compute a value of type +$B$, and the given value \lstinline!a:A! may be used for that. So, +${\cal CH}(A\rightarrow B)$ is true if and only if we are able to +compute a value of type $B$ when we are given a value of type $A$. +To translate this statement into the language of logical propositions, +we need to use the logical\index{logical implication} \textbf{implication}, +${\cal CH}(A)\Rightarrow{\cal CH}(B)$, which means that ${\cal CH}(B)$ +can be proved if we already have a proof of ${\cal CH}(A)$. So, the +rule for function types is: \[ {\cal CH}(A\rightarrow B)={\cal CH}(A)\Rightarrow{\cal CH}(B)\quad. \] @@ -373,7 +391,8 @@ \subsection{Type notation and ${\cal CH}$-propositions for standard type constru \forall(A,B).\,{\cal CH}\left(A\rightarrow(A\rightarrow B)\rightarrow B\right)\quad. \] The symbol $\forall$ means \textsf{``}for all\textsf{''} and is called the \index{universal quantifier (forall)@universal quantifier ($\forall$)}\textbf{universal -quantifier} in logic. +quantifier} in logic. We read $\forall A.\,{\cal CH}(B)$ as the proposition +\textsf{``}for all types $A$, we can compute a value of type $B$\textsf{''}. The type notation for the type signature of \lstinline!f! is written in one of the following ways: @@ -423,6 +442,16 @@ \subsection{Type notation and ${\cal CH}$-propositions for standard type constru and do not affect the computational properties of types. The type notation is designed to support nameless type expressions. +The rules just shown will allow us to express ${\cal CH}$-propositions +for complicated types via ${\cal CH}$-propositions for type parameters. +Then any type signature can be rewritten as a sequent that contains +${\cal CH}$-propositions only for the individual type parameters. + +In this way, we see a correspondence between fully parametric type +signatures and logical sequents that express the proposition \textsf{``}the +type signature can be implemented\textsf{''}. This is the first half of the +Curry-Howard correspondence. + Table~\ref{tab:ch-correspondence-type-notation-CH-propositions} summarizes the type notation and shows how to translate it into logic formulas with ${\cal CH}$-propositions. Apart from recursive types @@ -453,50 +482,48 @@ \subsection{Type notation and ${\cal CH}$-propositions for standard type constru \end{itemize} \begin{table} \begin{centering} -\begin{tabular}{|c|c|c|c|} +\begin{tabular}{|c|c|c|} \hline -\textbf{\small{}Type construction} & \textbf{\small{}Scala syntax} & \textbf{\small{}Type notation} & \textbf{\small{}${\cal CH}$-proposition}\tabularnewline +\textbf{\small{}Scala syntax} & \textbf{\small{}Type notation} & \textbf{\small{}${\cal CH}$-proposition}\tabularnewline \hline \hline -{\small{}type parameter} & \lstinline![A]! & $A$ & ${\cal CH}(A)$\tabularnewline +\lstinline![A]! & $A$ & ${\cal CH}(A)$\tabularnewline \hline -{\small{}product type (tuple)} & \lstinline!(A, B)! & $A\times B$ & ${\cal CH}(A)$ $\wedge$ ${\cal CH}(B)$\tabularnewline +\lstinline!(A, B)! & $A\times B$ & ${\cal CH}(A)$ $\wedge$ ${\cal CH}(B)$\tabularnewline \hline -{\small{}disjunctive type} & \lstinline!Either[A, B]! & $A+B$ & ${\cal CH}(A)$ $\vee$ ${\cal CH}(B)$\tabularnewline +\lstinline!Either[A, B]! & $A+B$ & ${\cal CH}(A)$ $\vee$ ${\cal CH}(B)$\tabularnewline \hline -{\small{}function type} & \lstinline!A => B! & $A\rightarrow B$ & ${\cal CH}(A)$ $\Rightarrow$ ${\cal CH}(B)$\tabularnewline +\lstinline!A => B! & $A\rightarrow B$ & ${\cal CH}(A)$ $\Rightarrow$ ${\cal CH}(B)$\tabularnewline \hline -{\small{}unit or a \textsf{``}named unit\textsf{''} type} & \lstinline!Unit! & $\bbnum 1$ & ${\cal CH}(\bbnum 1)=True$\tabularnewline +\lstinline!Unit! & $\bbnum 1$ & ${\cal CH}(\bbnum 1)=True$\tabularnewline \hline -{\small{}primitive type} & {\small{}}\lstinline!Int!{\small{}, }\lstinline!String!{\small{}, +{\small{}}\lstinline!Int!{\small{}, }\lstinline!String!{\small{}, ...} & {\small{}$\text{Int}$, $\text{String}$, ...} & ${\cal CH}(\text{Int})=True$\tabularnewline \hline -{\small{}void type} & \lstinline!Nothing! & $\bbnum 0$ & ${\cal CH}(\bbnum 0)=False$\tabularnewline +\lstinline!Nothing! & $\bbnum 0$ & ${\cal CH}(\bbnum 0)=False$\tabularnewline \hline -{\small{}value parameterized by type} & \lstinline!def f[A]: F[A]! & $f^{A}:F^{A}$ & $\forall A.\,{\cal CH}(F^{A})$\tabularnewline +\lstinline!def f[A]: F[A]! & $f^{A}:F^{A}$ & $\forall A.\,{\cal CH}(F^{A})$\tabularnewline \hline -{\small{}type with quantifier} & \lstinline!val f: [A] => F[A]!{\small{} (Scala 3)} & $f:\forall A.\,F^{A}$ & $\forall A.\,{\cal CH}(F^{A})$\tabularnewline +\lstinline!val f: [A] => F[A]!{\small{} (Scala 3)} & $f:\forall A.\,F^{A}$ & $\forall A.\,{\cal CH}(F^{A})$\tabularnewline \hline \end{tabular} \par\end{centering} -\caption{The correspondence\index{type notation} between type constructions -and ${\cal CH}$-propositions.\label{tab:ch-correspondence-type-notation-CH-propositions}} +\caption{The correspondence\index{type notation} between types and ${\cal CH}$-propositions.\label{tab:ch-correspondence-type-notation-CH-propositions}} \end{table} -\subsection{Solved examples: Type notation\index{solved examples}} +\subsection{Examples: Type notation\index{examples (with code)}} From now on, we will prefer to write types in the type notation rather than in the Scala syntax. The type notation allows us to write nameless -type expressions and makes the structure of complicated types more -clear, compared with the Scala syntax. Names of types and parts of -types are, of course, helpful for reminding programmers of the meaning -of data in a program. However, writing names for every part of every -type is not helpful for reasoning about the properties of types. Type -notation makes reasoning about types easier, as we will see throughout -this chapter. Once the programmer has finished deriving the necessary -types and verifying their properties, the type notation can be straightforwardly -translated into Scala code. Let us get some experience doing that. +type expressions and makes the structure of complicated types clearer +than in the Scala syntax. Names of types and parts of types are, of +course, helpful for reminding programmers of the meaning of data in +a program. However, writing names for every part of every type does +not help reasoning about the properties of types. Once the programmer +has finished deriving the necessary types and verifying their properties, +the type notation can be straightforwardly translated into Scala code. +Let us get some experience doing that. \subsubsection{Example \label{subsec:Example-ch-dupl-function}\ref{subsec:Example-ch-dupl-function}} @@ -513,11 +540,11 @@ \subsubsection{Example \label{subsec:Example-ch-dupl-function}\ref{subsec:Exampl def delta(x: ...) = (x, x) \end{lstlisting} To derive the most general type for \lstinline!delta!, first assume -\lstinline!x:A!, where \lstinline!A! is a type parameter; then the -tuple \lstinline!(x, x)! has type \lstinline!(A, A)!. We do not -see any constraints on the type parameter \lstinline!A!. So, \lstinline!A! -represents an arbitrary type and needs to be added to the type signature -of \lstinline!delta!: +\lstinline!x: A!, where \lstinline!A! is a type parameter; then +the tuple \lstinline!(x, x)! has type \lstinline!(A, A)!. We do +not see any constraints on the type parameter \lstinline!A!. So, +\lstinline!A! represents an arbitrary type and needs to be added +to the type signature of \lstinline!delta!: \begin{lstlisting} def delta[A](x: A): (A, A) = (x, x) \end{lstlisting} @@ -679,7 +706,7 @@ \subsection{Exercises: Type notation\index{exercises}} \subsubsection{Exercise \label{subsec:Exercise-type-notation-1}\ref{subsec:Exercise-type-notation-1}} Define a Scala disjunctive type \lstinline!Q[T, A]! corresponding -to the type notation: +to this type notation: \[ Q^{T,A}\triangleq\bbnum 1+T\times A+\text{Int}\times(T\rightarrow T)+\text{String}\times A\quad. \] @@ -687,8 +714,8 @@ \subsubsection{Exercise \label{subsec:Exercise-type-notation-1}\ref{subsec:Exerc \subsubsection{Exercise \label{subsec:Exercise-type-notation-2}\ref{subsec:Exercise-type-notation-2}} -Rewrite \lstinline!Either[(A, Int), Either[(A, Char), (A, Float)]]! -in the type notation. +Convert the type \lstinline!Either[(A, Int), Either[(A, Char), (A, Float)]]! +from Scala syntax to the type notation. \subsubsection{Exercise \label{subsec:Exercise-type-notation-3}\ref{subsec:Exercise-type-notation-3}} @@ -791,11 +818,13 @@ \subsection{Motivation and first examples\label{subsec:ch-Motivation-and-first-e \subsection{The rules of proof for ${\cal CH}$-propositions\label{subsec:The-rules-of-proof}} To derive the suitable logical axioms and proof rules systematically, -let us examine what makes a ${\cal CH}$-proposition true. A sequent -${\cal CH}(A)\vdash{\cal CH}(X)$ is true when a value of type $X$ -can be computed by fully parametric code that may only use a given -value of type $A$. To describe all possible ways of computing a value -of type $X$, we need to enumerate all possible ways of \emph{writing +let us examine what could make a sequent with ${\cal CH}$-propositions +true. + +A sequent ${\cal CH}(A)\vdash{\cal CH}(X)$ is true when a value of +type $X$ can be computed by fully parametric code that may only use +a given value of type $A$. To describe all possible ways of computing +a value of type $X$, we need to enumerate all possible ways of \emph{writing code} for a fully parametric function body. The requirement of full parametricity means that we are not allowed to use any specific types such as \lstinline!Int! or \lstinline!String!, any concrete values @@ -819,7 +848,7 @@ \subsection{The rules of proof for ${\cal CH}$-propositions\label{subsec:The-rul val x6: B = x5._2 // 6) Use a tuple. val x7: Either[A, B] = Right(x6) // 7) Create values of a disjunctive type. val x8 = x7 match { ... } // 8) Use values of a disjunctive type. -} // 9) Call f() itself recursively. Not included here because not supported by CH-propositions. +} // 9) Call f() itself recursively. Not included here because recursion is not supported by CH-propositions. \end{lstlisting} The proposition ${\cal CH}(X)$ is true if we can create a sequence of computed values such as \lstinline!x1!, \lstinline!x2!, ..., @@ -829,7 +858,7 @@ \subsection{The rules of proof for ${\cal CH}$-propositions\label{subsec:The-rul It is important that there are only a finite number of allowed code constructions. This defines rigorously the concept of \textsf{``}fully parametric -code\textsf{''}\index{fully parametric!code} and allows us to \emph{prove} +code\textsf{''}\index{fully parametric!code|textit} and allows us to \emph{prove} ${\cal CH}$-propositions. Reasoning about proof rules is easier with a compact notation. To @@ -845,13 +874,20 @@ \subsection{The rules of proof for ${\cal CH}$-propositions\label{subsec:The-rul can be translated into a combination of logic rules, which will produce a proof of the proposition ${\cal CH}(X)$. -In the following text, we will need to use the full formulation~(\ref{eq:ch-CH-proposition-def}) -of ${\cal CH}$-propositions and write them as sequents such as Eq.~(\ref{eq:ch-example-sequent}). -For brevity, we define $\alpha\triangleq{\cal CH}(A)$, $\beta\triangleq{\cal CH}(B)$, -etc. It is also customary to use the letter $\Gamma$ to denote a -set of premises, such as ${\cal CH}(X)$, ${\cal CH}(Y)$, ..., ${\cal CH}(Z)$ -in Eq.~(\ref{eq:ch-example-sequent}). So, we can write a shorter -formula $\Gamma\vdash\alpha$ instead of the sequent~(\ref{eq:ch-example-sequent}). +In this way, we will get a correspondence between proofs of sequents +and fully parametric programs. This is the second half of the Curry-Howard +correspondence. + +In the following text, we will need to write ${\cal CH}$-propositions +such as Eq.~(\ref{eq:ch-CH-proposition-def}) as sequents such as +Eq.~(\ref{eq:ch-example-sequent}). As we have seen, ${\cal CH}$-propositions +involving complicated type expressions can be always rewritten via +${\cal CH}$-propositions for individual type parameters (${\cal CH}(A)$, +${\cal CH}(B)$, etc.). So, we will only need sequents involving such +${\cal CH}$-propositions. For brevity, we denote $\alpha\triangleq{\cal CH}(A)$, +$\beta\triangleq{\cal CH}(B)$, etc. We will use the letter $\Gamma$ +to stand for a set of premises and write a shorter formula $\Gamma\vdash\alpha$ +instead of the sequent~(\ref{eq:ch-example-sequent}). With these notations, we list the rules for proving ${\cal CH}$-propositions and the corresponding code: @@ -1009,7 +1045,7 @@ \subsection{The rules of proof for ${\cal CH}$-propositions\label{subsec:The-rul \] The proof code can be written as: \[ -\text{Proof}\left(\Gamma\vdash\alpha\right)=\pi_{1}\left(\text{Proof}\left(\Gamma\vdash\alpha\wedge\beta\right)\right)\quad,\quad\quad\text{Proof}\left(\Gamma\vdash\beta\right)=\pi_{2}\left(\text{Proof}\left(\Gamma\vdash\alpha\wedge\beta\right)\right)\quad, +\text{Proof}\left(\Gamma\vdash\alpha\right)=\pi_{1}\left(\text{Proof}\left(\Gamma\vdash\alpha\wedge\beta\right)\right)\quad,\quad\text{Proof}\left(\Gamma\vdash\beta\right)=\pi_{2}\left(\text{Proof}\left(\Gamma\vdash\alpha\wedge\beta\right)\right)\quad, \] where we introduced the notation $\pi_{1}$ and $\pi_{2}$ to mean the Scala code \lstinline!_._1! and \lstinline!_._2!. @@ -1035,28 +1071,22 @@ \subsection{The rules of proof for ${\cal CH}$-propositions\label{subsec:The-rul \] The corresponding proof code can be written using the case class names \lstinline!Left! and \lstinline!Right! as: -\[ -\text{Proof}\left(\Gamma\vdash\alpha\vee\beta\right)=\text{Left}\,(\text{Proof}\left(\Gamma\vdash\alpha\right))\quad,\quad\quad\text{Proof}\left(\Gamma\vdash\alpha\vee\beta\right)=\text{Right}\,(\text{Proof}\left(\Gamma\vdash\beta\right))\quad. -\] +\begin{align*} +\text{Proof}\left(\Gamma\vdash\alpha\vee\beta\right) & =\text{Left}\,(\text{Proof}\left(\Gamma\vdash\alpha\right))\quad,\\ +\text{Proof}\left(\Gamma\vdash\alpha\vee\beta\right) & =\text{Right}\,(\text{Proof}\left(\Gamma\vdash\beta\right))\quad. +\end{align*} \paragraph{8) Use a disjunctive value} Pattern matching is the basic way of using a value of type \lstinline!Either[A, B]!: - -\begin{wrapfigure}{l}{0.42\columnwidth}% -\vspace{-1\baselineskip} \begin{lstlisting} val result: C = (e: Either[A, B]) match { case Left(x:A) => expr1(x) case Right(y:B) => expr2(y) } \end{lstlisting} - -\vspace{-1.2\baselineskip} -\end{wrapfigure}% - -\noindent Here, \lstinline!expr1(x)! is an expression of type \lstinline!C! +Here, \lstinline!expr1(x)! is an expression of type \lstinline!C! computed using \lstinline!x:A! and any previously computed values. Similarly, \lstinline!expr2(y)! is computed using \lstinline!y:B! and previous values. The values used in computation correspond to @@ -1109,18 +1139,11 @@ \subsection{Example: Proving a ${\cal CH}$-proposition and deriving code from type\label{subsec:Example:-Proving-a-ch-proposition}} We will implement a fully parametric function: - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} def f[A, B]: ((A => A) => B) => B = ??? \end{lstlisting} - -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -\noindent Implementing this function is the same as being able to -compute a value of type $F$, where $F$ is defined as: +Implementing this function is the same as being able to compute a +value of type $F$, where $F$ is defined as: \[ F\triangleq\forall(A,B).\,((A\rightarrow A)\rightarrow B)\rightarrow B\quad. \] @@ -1190,7 +1213,7 @@ \subsection{Example: Proving a ${\cal CH}$-proposition and deriving code from \begin{figure} \begin{centering} {\footnotesize{}}% -\fbox{\begin{minipage}[t]{0.5\columnwidth}% +\fbox{\begin{minipage}[t]{0.7\columnwidth}% \begin{center} {\footnotesize{}}{\footnotesize{}\Tree[ .$\emptyset\vdash((\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta $ [ .{rule \textsf{``}$\text{create function}$\textsf{''}} [ .$(\alpha\Rightarrow\alpha)\Rightarrow\beta\vdash\beta$ [ .{rule \textsf{``}$\text{use function}$\textsf{''}} [ .$(\alpha\Rightarrow\alpha)\Rightarrow\beta\vdash\alpha\Rightarrow\alpha$ [ .{rule \textsf{``}$\text{create function}$\textsf{''}} [ .$\Gamma_1,\alpha\vdash\alpha$ {axiom \textsf{``}$\text{use arg}$\textsf{''}} ] ] ] [ .$(\alpha\Rightarrow\alpha)\Rightarrow\beta\vdash(\alpha\Rightarrow\alpha)\Rightarrow\beta$ {axiom \textsf{``}$\text{use arg}$\textsf{''}} ] ] ] ] ]} \par\end{center}% @@ -1251,9 +1274,7 @@ \subsection{Example: Proving a ${\cal CH}$-proposition and deriving code from It turns out that one can replace the rules in Table~\ref{tab:Proof-rules-of-constructive-and-boolean} by a different but equivalent set of derivation rules that \emph{do} give an algorithm (called the \textsf{``}\index{LJT algorithm}LJT algorithm\textsf{''}, -see Section~\ref{app:The-LJT-algorithm} below). That algorithm first -translates a type signature containing tuples, disjunctive types, -and function types into a logical formula. Then the algorithm either +see Section~\ref{app:The-LJT-algorithm} below). That algorithm either finds that the given formula cannot be proved, or it finds a proof and infers code that has the given type signature. @@ -1308,10 +1329,10 @@ \subsection{Example: Proving a ${\cal CH}$-proposition and deriving code from The \lstinline!curryhoward! library will then print an error message, and compilation will fail. -As another example, let us verify that the type signature from Section~\ref{subsec:Example:-Failure-of-Boolean-logic} +As another example, let us verify that the type signature from Section~\ref{subsec:Motivation-and-outlook} is not implementable: \begin{lstlisting} -@ def bad[A, B, C](g: A => Either[B, C]): Either[A => B, A => C] = implement +@ def bad2[A, B, C](g: A => Either[B, C]): Either[A => B, A => C] = implement cmd4.sc:1: type (A => Either[B, C]) => Either[A => B, A => C] cannot be implemented \end{lstlisting} @@ -1328,4599 +1349,4587 @@ \subsection{Example: Proving a ${\cal CH}$-proposition and deriving code from to infer code from types by hand. We will practice doing that throughout the book. -\subsection{Failure of Boolean logic in reasoning about $\mathcal{CH}$-propositions\label{subsec:Example:-Failure-of-Boolean-logic}} - -Programmers are most familiar with the \index{Boolean logic}Boolean -logic whose operations are written in Scala as \lstinline!x && y! -(conjunction), \lstinline!x || y! (disjunction), and \lstinline*!x* -(negation). However, it turns out that the Boolean logic does \emph{not} -always produce correct conclusions when reasoning about ${\cal CH}$-propositions. -One needs to use the constructive logic to reason correctly about -implementable type signatures. +\subsection{The LJT algorithm\label{app:The-LJT-algorithm}} -Let us nevertheless briefly look at how Boolean logic would handle -that reasoning. In the Boolean logic, each proposition ($\alpha$, -$\beta$, ...) is either $True$ or $False$. The operations are $\alpha\wedge\beta$ -(conjunction), $\alpha\vee\beta$ (disjunction), and $\neg\alpha$ -(negation). The \textbf{implication} \index{implication (in logic)} -($\Rightarrow$) is defined through other operations by: -\begin{equation} -\left(\alpha\Rightarrow\beta\right)\triangleq\left((\neg\alpha)\vee\beta\right)\quad.\label{eq:ch-definition-of-implication-in-Boolean-logic} -\end{equation} +In the previous section, we saw an example showing that the rules +in Table~\ref{tab:Proof-rules-of-constructive-and-boolean} do not +provide an algorithm for finding a proof for a given sequent. -To verify whether a formula is true in the Boolean logic, we can substitute -either $True$ or $False$ into every variable and check if the formula -has the value $True$ in all possible cases. The result can be arranged -into a \index{truth table}truth table. The formula is true if all -values in its truth table are $True$. +To illustrate this problem on another example, let us try proving +the sequent: +\[ +A,B\vee C\vdash(A\wedge B)\vee C\quad. +\] +We expect that this sequent is provable because we can write the corresponding +Scala code: +\begin{lstlisting} +def f[A, B, C](a: A): Either[B, C] => Either[(A, B), C] = { + case Left(b) => Left((a, b)) + case Right(c) => Right(c) +} +\end{lstlisting} +How can we obtain a proof of this sequent according to the rules in +Table~\ref{tab:Proof-rules-of-constructive-and-boolean}? We find +that we could potentially apply the rules \textsf{``}create \lstinline!Left!\textsf{''}, +\textsf{``}create \lstinline!Right!\textsf{''}, \textsf{``}use \lstinline!Either!\textsf{''}, and +\textsf{``}use function\textsf{''}. However, no matter what rule we choose, we will +get stuck at the next step. Let us see why: -Disjunction, conjunction, negation, and implication operations have -the following truth table: -\begin{center} -{\small{}}% -\begin{tabular}{|c|c|c|c|c|c|} -\hline -{\small{}$\alpha$} & {\small{}$\beta$} & \textbf{\small{}$\alpha\vee\beta$} & \textbf{\small{}$\alpha\wedge\beta$} & \textbf{\small{}$\neg\alpha$} & \textbf{\small{}$\alpha\Rightarrow\beta$}\tabularnewline -\hline -\hline -{\small{}$True$} & {\small{}$True$} & {\small{}$True$} & {\small{}$True$} & {\small{}$False$} & {\small{}$True$}\tabularnewline -\hline -{\small{}$True$} & {\small{}$False$} & {\small{}$True$} & {\small{}$False$} & {\small{}$False$} & {\small{}$False$}\tabularnewline -\hline -{\small{}$False$} & {\small{}$True$} & {\small{}$True$} & {\small{}$False$} & {\small{}$True$} & {\small{}$True$}\tabularnewline -\hline -{\small{}$False$} & {\small{}$False$} & {\small{}$False$} & {\small{}$False$} & {\small{}$True$} & {\small{}$True$}\tabularnewline -\hline -\end{tabular}{\small\par} -\par\end{center} +To apply \textsf{``}create \lstinline!Left!\textsf{''}, we first need to prove the +sequent $A,B\vee C\vdash A\wedge B$. But this sequent cannot be proved: +we do not necessarily have values of both types $A$ and $B$ if we +are only given values of type $A$ and of type \lstinline!Either[B, C]!. +To apply \textsf{``}create \lstinline!Right!\textsf{''}, we need to prove the sequent +$A,B\vee C\vdash C$. Again, we find that this sequent cannot be proved. +The next choice is the rule \textsf{``}use \lstinline!Either!\textsf{''} that matches +any goal of the sequent as the proposition $\gamma$. But we are then +required to choose two new propositions ($\alpha$ and $\beta$) such +that we can prove $A,B\vee C\vdash\alpha\vee\beta$ as well as $A,B\vee C,\alpha\vdash(A\wedge B)\vee C$ +and $A,B\vee C,\beta\vdash(A\wedge B)\vee C$. It is not clear how +we should choose $\alpha$ and $\beta$ in order to make progress +in the proof. The remaining rule, \textsf{``}use function\textsf{''}, similarly requires +us to choose a new proposition $\alpha$ such that we can prove $A,B\vee C\vdash\alpha$ +and $A,B\vee C\vdash\alpha\Rightarrow((A\wedge B)\vee C)$. The rules +give us no guidance for choosing $\alpha$ appropriately. -Using this table, we find that the formula $\alpha\Rightarrow\alpha$ -has the value $True$ in all cases, whether $\alpha$ itself is $True$ -or $False$. This check is sufficient to show that $\forall\alpha.\,\alpha\Rightarrow\alpha$ -is true in Boolean logic. +To see that the rules in Table~\ref{tab:Proof-rules-for-constructive-logic} +are not helpful for proof search, note that the rules \textsf{``}use function\textsf{''} +and \textsf{``}use \lstinline!Either!\textsf{''} require us to choose new unknown +propositions and to prove more complicated sequents than the ones +we had before. For instance, the rule \textsf{``}use function\textsf{''} gives a proof +of $\Gamma\vdash\beta$ only if we first choose some other proposition +$\alpha$ and prove the sequents $\Gamma\vdash\alpha$ and $\Gamma\vdash\alpha\Rightarrow\beta$. +The rule does not say how to choose the proposition $\alpha$ correctly. +We need to guess the correct $\alpha$ by trial and error. Even after +choosing $\alpha$ in some way, we will have to prove a more complicated +sequent ($\Gamma\vdash\alpha\Rightarrow\beta$). It is not guaranteed +that we are getting closer to finding the proof of the initial sequent +($\Gamma\vdash\beta$). -Here is the truth table for the formulas $\forall(\alpha,\beta).\,(\alpha\wedge\beta)\Rightarrow\alpha$ -and $\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\wedge\beta)$. -The first formula is true in Boolean logic since all values in its -column are $True$, while the second formula is not true since one -value in the last column is $False$: -\begin{center} -{\small{}}% -\begin{tabular}{|c|c|c|c|c|} -\hline -{\small{}$\alpha$} & {\small{}$\beta$} & \textbf{\small{}$\alpha\wedge\beta$} & {\small{}$(\alpha\wedge\beta)\Rightarrow\alpha$} & {\small{}$\alpha\Rightarrow(\alpha\wedge\beta)$}\tabularnewline -\hline -\hline -{\small{}$True$} & {\small{}$True$} & {\small{}$True$} & {\small{}$True$} & {\small{}$True$}\tabularnewline -\hline -{\small{}$True$} & {\small{}$False$} & {\small{}$False$} & {\small{}$True$} & {\small{}$False$}\tabularnewline -\hline -{\small{}$False$} & {\small{}$True$} & {\small{}$False$} & {\small{}$True$} & {\small{}$True$}\tabularnewline -\hline -{\small{}$False$} & {\small{}$False$} & {\small{}$False$} & {\small{}$True$} & {\small{}$True$}\tabularnewline -\hline -\end{tabular}{\small\par} -\par\end{center} +It is far from obvious how to overcome that difficulty. Mathematicians +have studied the constructive logic for more than 60 years, trying +to replace the rules in Table~\ref{tab:Proof-rules-of-constructive-and-boolean} +by a different but equivalent set of derivation rules that require +no guessing when looking for a proof. The first partial success came +in 1935 with an algorithm called \textsf{``}LJ\textsf{''}.\footnote{See \texttt{\href{https://en.wikipedia.org/wiki/Sequent_calculus\#Overview}{https://en.wikipedia.org/wiki/Sequent\_calculus\#Overview}}} +The LJ algorithm still has a significant problem: one of its derivation +rules may be applied infinitely many times. So, the LJ algorithm is +not guaranteed to terminate without some heuristics for avoiding an +infinite loop. A terminating version of the LJ algorithm, called \index{LJT algorithm}LJT, +was formulated in 1992.\footnote{An often cited paper by R.~Dyckhoff\index{Roy Dyckhoff} is \texttt{\href{https://philpapers.org/rec/DYCCSC}{https://philpapers.org/rec/DYCCSC}}. +For the history of that research, see \texttt{\href{https://research-repository.st-andrews.ac.uk/handle/10023/8824}{https://research-repository.st-andrews.ac.uk/handle/10023/8824}}} -Table~\ref{tab:Logical-formulas-Boolean-theorems} shows more examples -of logical formulas that are true in Boolean logic. Each formula is -first written in terms of ${\cal CH}$-propositions (we denote $\alpha\triangleq{\cal CH}(A)$ -and $\beta\triangleq{\cal CH}(B)$ for brevity) and then as a Scala -type signature of a function. So, all these type signatures \emph{can} -be implemented. +We will first present the LJ algorithm. Although that algorithm does +not guarantee termination, it is simpler to understand and to apply +by hand. Then we will show how to modify the LJ algorithm in order +to obtain the always-terminating LJT algorithm. -\begin{table}[h] -\begin{centering} -\begin{tabular}{|c|c|c|} -\hline -\textbf{\small{}Logic formula} & \textbf{\small{}Type formula} & \textbf{\small{}Scala code}\tabularnewline -\hline -\hline -{\footnotesize{}$\forall\alpha.\,\alpha\Rightarrow\alpha$} & {\footnotesize{}$\forall A.\,A\rightarrow A$} & \lstinline!def id[A](x: A): A = x!\tabularnewline -\hline -{\footnotesize{}$\forall\alpha.\,\alpha\Rightarrow True$} & {\footnotesize{}$\forall A.\,A\rightarrow\bbnum 1$} & \lstinline!def toUnit[A](x: A): Unit = ()!\tabularnewline -\hline -{\footnotesize{}$\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\vee\beta)$} & {\footnotesize{}$\forall(A,B).\,A\rightarrow A+B$} & \lstinline!def toL[A, B](x: A): Either[A, B] = Left(x)!\tabularnewline -\hline -{\footnotesize{}$\forall(\alpha,\beta).\,(\alpha\wedge\beta)\Rightarrow\alpha$} & {\footnotesize{}$\forall(A,B).\,A\times B\rightarrow A$} & \lstinline!def first[A, B](p: (A, B)): A = p._1!\tabularnewline -\hline -{\footnotesize{}$\forall(\alpha,\beta).\,\alpha\Rightarrow(\beta\Rightarrow\alpha)$} & {\footnotesize{}$\forall(A,B).\,A\rightarrow(B\rightarrow A)$} & \lstinline!def const[A, B](x: A): B => A = (_ => x)!\tabularnewline -\hline -\end{tabular} -\par\end{centering} -\caption{Examples of logical formulas that are true theorems in Boolean logic.\label{tab:Logical-formulas-Boolean-theorems}} -\end{table} +\subsubsection*{The LJ algorithm} -Table~\ref{tab:Logical-formulas-not-Boolean-theorems} some examples -of formulas that are \emph{not true} in Boolean logic. Translated -into type formulas and then into Scala, these formulas yield type -signatures that \emph{cannot} be implemented by fully parametric functions. +Figure~\ref{fig:Rules-of-the-LJ-algorithm} shows the LJ algorithm\textsf{'}s +axioms and derivation rules. Each rule says that the bottom sequent +will be proved if proofs are given for sequent(s) at the top. For +each possible sub-expression (conjunction $X\wedge Y$, disjunction +$X\vee Y$, and implication $X\Rightarrow Y$) there is one rule where +that sub-expression is a premise (at \textsf{``}left\textsf{''}) and one rule where +that sub-expression is the goal (at \textsf{``}right\textsf{''}). Those sub-expressions +are shown in blue in Figure~\ref{fig:Rules-of-the-LJ-algorithm} +to help us look for a proof. To find out which rules apply, we match +some part of the sequent with a blue sub-expression. -\begin{table}[h] +\begin{figure} \begin{centering} -\begin{tabular}{|c|c|c|} -\hline -\textbf{\small{}Logic formula} & \textbf{\small{}Type formula} & \textbf{\small{}Scala type signature}\tabularnewline -\hline -\hline -{\footnotesize{}$\forall\alpha.\,True\Rightarrow\alpha$} & {\footnotesize{}$\forall A.\,\bbnum 1\rightarrow A$} & \lstinline!def f[A](x: Unit): A!\tabularnewline -\hline -{\footnotesize{}$\forall(\alpha,\beta).\,(\alpha\vee\beta)\Rightarrow\alpha$} & {\footnotesize{}$\forall(A,B).\,A+B\rightarrow A$} & \lstinline!def f[A,B](x: Either[A, B]): A!\tabularnewline -\hline -{\footnotesize{}$\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\wedge\beta)$} & {\footnotesize{}$\forall(A,B).\,A\rightarrow A\times B$} & \lstinline!def f[A,B](p: A): (A, B)!\tabularnewline -\hline -{\footnotesize{}$\forall(\alpha,\beta).\,(\alpha\Rightarrow\beta)\Rightarrow\alpha$} & {\footnotesize{}$\forall(A,B).\,(A\rightarrow B)\rightarrow A$} & \lstinline!def f[A,B](x: A => B): A!\tabularnewline -\hline -\end{tabular} +\fbox{\begin{minipage}[t]{0.7\linewidth}% +\begin{align*} +\frac{}{\Gamma,X\vdash{\color{blue}X}}~(\text{Id})\qquad & \qquad\frac{}{\Gamma\vdash{\color{blue}\top}}~(\text{True})\\ +\frac{\Gamma,A\Rightarrow B\vdash A\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\Rightarrow B}\vdash C}~(\text{Left}\Rightarrow)\qquad & \qquad\frac{\Gamma,A\vdash B}{\Gamma\vdash{\color{blue}A\Rightarrow B}}~(\text{Right}\Rightarrow)\\ +\frac{\Gamma,A_{i}\vdash C}{\Gamma,{\color{blue}A_{1}\wedge A_{2}}\vdash C}~(\text{Left}\wedge_{i})\qquad & \qquad\frac{\Gamma\vdash A\quad\quad\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\wedge B}}~(\text{Right}\wedge)\\ +\frac{\Gamma,A\vdash C\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\vee B}\vdash C}~(\text{Left}\vee)\qquad & \qquad\frac{\Gamma\vdash A_{i}}{\Gamma\vdash{\color{blue}A_{1}\vee A_{2}}}~(\text{Right}\vee_{i}) +\end{align*} +% +\end{minipage}} \par\end{centering} -\caption{Examples of logical formulas that are \emph{not} true in Boolean logic.\label{tab:Logical-formulas-not-Boolean-theorems}} -\end{table} +\caption{Axioms and derivation rules of the LJ algorithm. Each of the rules +\textquotedblleft ($\text{Left}\wedge_{i}$)\textquotedblright{} and +\textquotedblleft ($\text{Right}\vee_{i}$)\textquotedblright{} have +two versions, with $i=1$ or $i=2$. \label{fig:Rules-of-the-LJ-algorithm}} +\end{figure} -At first sight, it may appear from these examples that whenever a -logical formula is true in Boolean logic, the corresponding type signature -can be implemented in code, and vice versa. However, this is \emph{incorrect}: -the rules of Boolean logic are not fully suitable for reasoning about -types in a functional language. We will now show some examples of -formulas that are true in Boolean logic but correspond to unimplementable -type signatures. +It turns out that the rules in Figure~\ref{fig:Rules-of-the-LJ-algorithm} +are \emph{equivalent} to the rules in Table~\ref{tab:Proof-rules-for-constructive-logic}. +The proof is beyond the scope of this book. We only remark that this +equivalence is far from obvious. To prove it, one needs to demonstrate +that any sequent derived through the first set of rules is also derivable +through the second set, and vice versa. -The first example is given by the following type: -\begin{equation} -\forall(A,B,C).\,\left(A\rightarrow B+C\right)\rightarrow\left(A\rightarrow B\right)+\left(A\rightarrow C\right)\quad,\label{eq:ch-example-boolean-bad-type} -\end{equation} -which corresponds to the Scala type signature: -\begin{lstlisting} -def bad[A, B, C](g: A => Either[B, C]): Either[A => B, A => C] = ??? -\end{lstlisting} -The function \lstinline!bad! \emph{cannot} be implemented via fully -parametric code. To see why, consider that the only available data -is a function $g^{:A\rightarrow B+C}$, which returns values of type -$B$ or $C$ depending (in some unknown way) on the input value of -type $A$. The function \lstinline!bad! must return either a function -of type $A\rightarrow B$ or a function of type $A\rightarrow C$. -How can the code of \lstinline!bad! make that decision? The only -input data is the function $g$ that takes an argument of type $A$. -We could imagine applying $g$ to various arguments of type $A$ and -to see whether $g$ returns a $B$ or a $C$. However, the type $A$ -is arbitrary, and a fully parametric function cannot produce a value -of type $A$ in order to apply $g$ to it. So, the decision about -whether to return $A\rightarrow B$ or $A\rightarrow C$ must be independent -of $g$. That decision must be hard-coded in the function \lstinline!bad!. - -Suppose we hard-coded the decision to return a function of type $A\rightarrow B$. -How would we create a function of type $A\rightarrow B$ in the body -of \lstinline!bad!? Given a value $x^{:A}$ of type $A$, we would -need to compute some value of type $B$. Since the type $B$ is arbitrary -(it is a type parameter), we cannot produce a value of type $B$ from -scratch. The only potential source of values of type $B$ is the given -function $g$. The only way of using $g$ is to apply it to $x^{:A}$. -However, for some $x$, the value $g(x)$ may be of the form \lstinline!Right(c)!, -where \lstinline!c! is of type $C$. In that case, we will have a -value of type $C$, not $B$. So, in general, we cannot guarantee -that we can always obtain a value of type $B$ from a given value -$x^{:A}$. This means we cannot build a function of type $A\rightarrow B$ -out of the function $g$. Similarly, we cannot build a function of -type $A\rightarrow C$ out of $g$. - -Whether we decide to return $A\rightarrow B$ or $A\rightarrow C$, -we will not be able to return a value of the required type, as we -just saw. We must conclude that we cannot implement \lstinline!bad! -as a fully parametric function. +To illustrate the LJ algorithm, let us prove the sequent~(\ref{eq:ch-example-sequent-2}). +Denote that sequent by $S_{0}$: +\[ +S_{0}\triangleq\emptyset\vdash\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\Rightarrow\beta\quad. +\] + Since the goal of $S_{0}$ contains an implication, we use the rule +\textsf{``}($\text{Right}\Rightarrow$)\textsf{''} and get a sequent $S_{1}$: +\[ +S_{1}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\beta\quad. +\] +Now the implication is in the premise, so we use the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''} +and get two new sequents: +\[ +S_{2}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\alpha\Rightarrow\alpha\quad,\quad\quad S_{3}\triangleq\beta\vdash\beta\quad. +\] +Sequent $S_{3}$ follows from the \textsf{``}(Id)\textsf{''} axiom, so it remains +to prove $S_{2}$. Since $S_{2}$ contains an implication both as +a premise and as the goal, we may apply either the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''} +or the rule \textsf{``}($\text{Right}\Rightarrow$)\textsf{''}. We choose to apply +\textsf{``}($\text{Left}\Rightarrow$)\textsf{''} and get two new sequents: +\[ +S_{4}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\alpha\Rightarrow\alpha\quad,\quad\quad S_{5}:\beta\vdash\alpha\Rightarrow\alpha\quad. +\] +Notice that $S_{4}=S_{2}$. So, our proof search is getting into an +infinite loop trying to prove the same sequent $S_{2}$ over and over +again. We can prove $S_{5}$ but this will not help us break the loop. -We could try to switch between $A\rightarrow B$ and $A\rightarrow C$ -depending on a given value of type $A$. This idea, however, means -that we are working with a different type signature: +Once we recognize the problem, we backtrack to the point where we +chose to apply \textsf{``}($\text{Left}\Rightarrow$)\textsf{''} to $S_{2}$. That +was a bad choice, so let us instead apply \textsf{``}($\text{Right}\Rightarrow$)\textsf{''} +to $S_{2}$. This yields a new sequent $S_{6}$: \[ -\forall(A,B,C).\,\left(A\rightarrow B+C\right)\rightarrow A\rightarrow\left(A\rightarrow B\right)+\left(A\rightarrow C\right)\quad. +S_{6}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta,\alpha\vdash\alpha\quad. \] -This type signature \emph{can} be implemented, for instance, by this -Scala code: -\begin{lstlisting} -def q[A, B, C](g: A => Either[B, C]): A => Either[A => B, A => C] = { a => - g(a) match { - case Left(b) => Left(_ => b) - case Right(c) => Right(_ => c) - } +This sequent follows from the \textsf{``}(Id)\textsf{''}axiom. There are no more sequents +to prove, so the proof of $S_{0}$ is finished. It can be drawn as +a \index{proof tree}\textbf{proof tree} like this: +\[ +\xymatrix{\xyScaleY{1.5pc}\xyScaleX{4pc} & & & (\text{Id})\\ +\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[r]\sp(0.5){S_{2}}\ar[ru]\sp(0.6){S_{3}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){S_{6}} & (\text{Id}) } -\end{lstlisting} -But this is not the required type signature~(\ref{eq:ch-example-boolean-bad-type}). - -Now let us convert the type signature~(\ref{eq:ch-example-boolean-bad-type}) -into a ${\cal CH}$-proposition: -\begin{align} - & \forall(\alpha,\beta,\gamma).\,\left(\alpha\Rightarrow\left(\beta\vee\gamma\right)\right)\Rightarrow\left(\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)\right)\quad,\label{eq:abc-example-classical-logic-bad}\\ -\text{where we denoted}\quad & \alpha\triangleq{\cal CH}(A),\quad\beta\triangleq{\cal CH}(B),\quad\gamma\triangleq{\cal CH}(C)\quad.\nonumber -\end{align} -It turns out that this formula is true in Boolean logic. To prove -this, we need to show that Eq.~(\ref{eq:abc-example-classical-logic-bad}) -is equal to $True$ for any Boolean values of the variables $\alpha$, -$\beta$, $\gamma$. One way is to rewrite the expression~(\ref{eq:abc-example-classical-logic-bad}) -using the rules of Boolean logic, such as Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic}): -\begin{align*} - & \gunderline{\alpha\Rightarrow}\left(\beta\vee\gamma\right)\\ -{\color{greenunder}\text{definition of }\Rightarrow\text{ via Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & \quad=(\neg\alpha)\vee\beta\vee\gamma\quad,\\ - & \gunderline{\left(\alpha\Rightarrow\beta\right)}\vee\gunderline{\left(\alpha\Rightarrow\gamma\right)}\\ -{\color{greenunder}\text{definition of }\Rightarrow\text{ via Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & \quad=\gunderline{(\neg\alpha)}\vee\beta\vee\gunderline{(\neg\alpha)}\vee\gamma\\ -{\color{greenunder}\text{property }x\vee x=x\text{ in Boolean logic}:}\quad & \quad=(\neg\alpha)\vee\beta\vee\gamma\quad, -\end{align*} -showing that $\alpha\Rightarrow(\beta\vee\gamma)$ is in fact \emph{equal} -to $\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)$ -in Boolean logic. - -Let us also give a proof via truth-value reasoning. The only possibility -for an implication $X\Rightarrow Y$ to be $False$ is when $X=True$ -and $Y=False$. So, Eq.~(\ref{eq:abc-example-classical-logic-bad}) -can be $False$ only if $\left(\alpha\Rightarrow(\beta\vee\gamma)\right)=True$ -and $\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)=False$. -A disjunction can be false only when both parts are false; so we must -have both $\left(\alpha\Rightarrow\beta\right)=False$ and $\left(\alpha\Rightarrow\gamma\right)=False$. -This is only possible if $\alpha=True$ and $\beta=\gamma=False$. -But, with these value assignments, we find $\left(\alpha\Rightarrow(\beta\vee\gamma)\right)=False$ -rather than $True$ as we assumed. It follows that we cannot ever -make Eq.~(\ref{eq:abc-example-classical-logic-bad}) equal to $False$. -So, Eq.~(\ref{eq:abc-example-classical-logic-bad}) is true in Boolean -logic. +\] +The nodes of the proof tree are axioms or derivation rules, and the +edges are intermediate sequents required by the rules. Some rule nodes +branch into several sequents because some rules require more than +one new sequent to be proved. The leaves of the tree are axioms that +do not require proving any further sequents. -\section{Equivalence of types} +\subsubsection*{Extracting code from proofs} -We found a correspondence between types, code, logical propositions, -and proofs, which is known as the \textbf{Curry-Howard correspondence}\index{Curry-Howard correspondence}. -An example of the CH correspondence is that a proof of the logical -proposition: -\begin{equation} -\forall(\alpha,\beta).\,\alpha\Rightarrow\left(\beta\Rightarrow\alpha\right)\label{eq:ch-proposition-example-2} -\end{equation} -corresponds to the code of the following function: -\begin{lstlisting} -def f[A, B]: A => (B => A) = { x => _ => x } -\end{lstlisting} -With the CH correspondence in mind, we may say that the \emph{existence} -of the code \lstinline!x => _ => x! with the type $A\rightarrow(B\rightarrow A)$ -\textsf{``}is\textsf{''} a proof of the logical formula~(\ref{eq:ch-proposition-example-2}), -because it shows how to compute a value of type $\forall(A,B).\,A\rightarrow B\rightarrow A$. +According to the Curry-Howard correspondence, a sequent (such as $A,B,...,C\vdash X$) +represents the task of writing a fully parametric code expression +of type $X$ that uses some given values of types $A$, $B$, ..., +$C$. The sequent is true (i.e., can be proved) if that code expression +can be found. So, the code serves as an \textsf{``}evidence of proof\textsf{''} for +the sequent. -The Curry-Howard correspondence maps logic formulas such as $(\alpha\vee\beta)\wedge\gamma$ -into type expressions such as $\left(A+B\right)\times C$. We have -seen that types behave similarly to logic formulas in one respect: -A logic formula is a true theorem of constructive logic when the corresponding -type signature can be implemented as a fully parametric function, -and vice versa. +\begin{figure} +\begin{centering} +{\small{}}% +\noindent\fbox{\begin{minipage}[t]{1\columnwidth - 2\fboxsep - 2\fboxrule}% +{\small{} +\begin{align*} +\frac{}{\Gamma,A\vdash{\color{blue}A}}~(\text{Id})\quad & \quad\text{Proof}\,(\Gamma,A\vdash A)_{\text{given }p^{:\Gamma},x^{:A}}=x\\ +\frac{}{\Gamma\vdash{\color{blue}\top}}~(\text{True})\quad & \quad\text{Proof}\,(\Gamma\vdash\top)_{\text{given }p^{:\Gamma}}=1\\ +\frac{\Gamma,A\Rightarrow B\vdash A\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\Rightarrow B}\vdash C}~(\text{Left}\Rightarrow)\quad & \quad\text{Proof}\,(\Gamma,A\Rightarrow B\vdash C)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}\\ + & \quad\quad=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p,b^{:B}}\\ +\text{where} & \quad b^{:B}\triangleq q\big(\text{Proof}\,(\Gamma,A\Rightarrow B\vdash A)_{\text{given }p,q}\big)\\ +\frac{\Gamma,A\vdash B}{\Gamma\vdash{\color{blue}A\Rightarrow B}}~(\text{Right}\Rightarrow)\quad & \quad\text{Proof}\,(\Gamma\vdash A\Rightarrow B)_{\text{given }p^{:\Gamma}}\\ + & \quad\quad=x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash B)_{\text{given }p^{:\Gamma},x^{:A}}\\ +\frac{\Gamma,A\vdash C}{\Gamma,{\color{blue}A\wedge B}\vdash C}~(\text{Left}\wedge_{1})\quad & \quad\text{Proof}\,(\Gamma,A\wedge B\vdash C)_{\text{given }p^{:\Gamma},(a^{:A}\times b^{:B})}\\ + & \quad\quad=\text{Proof}\,(\Gamma,A\vdash C)_{\text{given }p^{:\Gamma},a^{:A}}\\ +\frac{\Gamma,B\vdash C}{\Gamma,{\color{blue}A\wedge B}\vdash C}~(\text{Left}\wedge_{2})\quad & \quad\text{Proof}\,(\Gamma,A\wedge B\vdash C)_{\text{given }p^{:\Gamma},(a^{:A}\times b^{:B})}\\ + & \quad\quad=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p^{:\Gamma},b^{:B}}\\ +\frac{\Gamma\vdash A\quad\quad\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\wedge B}}~(\text{Right}\wedge)\quad & \quad\text{Proof}\,(\Gamma\vdash A\wedge B)_{\text{given }p^{:\Gamma}}\\ + & \quad\quad=\text{Proof}\,(\Gamma\vdash A)_{\text{given }p^{:\Gamma}}\\ + & \quad\quad\quad\times\text{Proof}\,(\Gamma\vdash B)_{\text{given }p^{:\Gamma}}\\ +\frac{\Gamma,A\vdash C\quad\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\vee B}\vdash C}~(\text{Left}\vee)\quad & \quad\text{Proof}\,(\Gamma,A\vee B\vdash C)_{\text{given }p^{:\Gamma},q^{:A+B}}\\ + & \quad\quad=q\triangleright\begin{array}{|c||c|} + & C\\ +\hline A & x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash C)_{\text{given }p,x}\\ +B & y^{:B}\rightarrow\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p,y} +\end{array}\\ +\frac{\Gamma\vdash A}{\Gamma\vdash{\color{blue}A\vee B}}~(\text{Right}\vee_{1})\quad & \quad\text{Proof}\,(\Gamma\vdash A\vee B)_{\text{given }p^{:\Gamma}}=\text{Proof}\,(\Gamma\vdash A)+\bbnum 0^{:B}\\ +\frac{\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\vee B}}~(\text{Right}\vee_{2})\quad & \quad\text{Proof}\,(\Gamma\vdash A\vee B)_{\text{given }p^{:\Gamma}}=\bbnum 0^{:A}+\text{Proof}\,(\Gamma\vdash B) +\end{align*} +}% +\end{minipage}}{\small\par} +\par\end{centering} +\caption{\label{fig:proof-transformers-for-LJ-rules}Proof transformers for +the rules of the LJ algorithm.} +\end{figure} -It turns out that the similarity ends here. In other respects, type -expressions behave as \emph{arithmetic} expressions and not as logic -formulas. For this reason, the type notation used in this book denotes -disjunctive types by $A+B$ and tuples by $A\times B$, which is designed -to remind us of arithmetic expressions (such as $1+2$ and $2\times3$) -rather than of logical formulas (such as $A\vee B$ and $A\wedge B$). +In the previous subsection, we have found a proof of the sequent $S_{0}$, +which represents the task of writing a fully parametric function with +type signature $(\left(A\rightarrow A\right)\rightarrow B)\rightarrow B$). +Let us now see how we can extract the code of that function from the +proof of the sequent $S_{0}$. -An important use of the type notation is for writing equations with -types. Can we use the arithmetic intuition for writing type equations -such as: -\begin{equation} -\left(A+B\right)\times C=A\times C+B\times C\quad?\label{eq:ch-example-distributive} -\end{equation} -In this section, we will learn how to check whether one type expression -is equivalent to another. +We start from the leaves of the proof tree and move step by step towards +the initial sequent. At each step, we shorten the proof tree by replacing +some sequent by its corresponding evidence-of-proof code. Eventually +we will replace the initial sequent by its corresponding code. Let +us see how this procedure works for the proof tree of the sequent +$S_{0}$ shown in the previous section. -\subsection{Logical identity does not correspond to type equivalence\label{subsec:Logical-identity-not-type-equivalence}} +Since the leaves are axioms, let us write the code corresponding to +each axiom of LJ: +\begin{align*} + & \frac{}{\Gamma,X\vdash X}~(\text{Id})\quad:\quad\quad\text{Proof}\,(\Gamma,X\vdash X)_{\text{given }p^{:\Gamma},x^{:X}}=x\quad;\\ + & \frac{}{\Gamma\vdash\top}~(\text{True})\quad:\quad\quad\text{Proof}\,(\Gamma\vdash\top)_{\text{given }p^{:\Gamma}}=1\quad. +\end{align*} +Here we denote explicitly the values (such as $p$ and $x$) given +as premises to the sequent. The notation $p^{:\Gamma}$ means all +values given in the set of premises $\Gamma$. Below we will assume +that the propositions $\alpha$ and $\beta$ correspond to types $A$ +and $B$; that is, $\alpha\triangleq{\cal CH}(A)$ and $\beta\triangleq{\cal CH}(B)$. -The CH correspondence maps Eq.~(\ref{eq:ch-example-distributive}) -into the logic formula: -\begin{equation} -\forall(A,B,C).\,\left(A\vee B\right)\wedge C=\left(A\wedge C\right)\vee\left(B\wedge C\right)\quad.\label{eq:ch-example-distributive-1} -\end{equation} -This formula is the well-known \textsf{``}distributive law\textsf{''}\footnote{See \texttt{\href{https://en.wikipedia.org/wiki/Distributive_property\#Rule_of_replacement}{https://en.wikipedia.org/wiki/Distributive\_property\#Rule\_of\_replacement}}} -valid in Boolean logic as well as in the constructive logic. Since -a logical equation $P=Q$ means $P\Rightarrow Q$ and $Q\Rightarrow P$, -the distributive law~(\ref{eq:ch-example-distributive-1}) means -that the two formulas hold: -\begin{align} - & \forall(A,B,C).\,\left(A\vee B\right)\wedge C\Rightarrow\left(A\wedge C\right)\vee\left(B\wedge C\right)\quad,\label{eq:ch-example-distributive-1a}\\ - & \forall(A,B,C).\,\left(A\wedge C\right)\vee\left(B\wedge C\right)\Rightarrow\left(A\vee B\right)\wedge C\quad.\label{eq:ch-example-distributive-1b} -\end{align} -The CH correspondence maps these logical formulas to fully parametric -functions with types: -\begin{lstlisting} -def f1[A, B, C]: ((Either[A, B], C)) => Either[(A, C), (B, C)] = ??? -def f2[A, B, C]: Either[(A, C), (B, C)] => (Either[A, B], C) = ??? -\end{lstlisting} -In the type notation, these type signatures are written as: +The leaves in the proof tree for $S_{0}$ are the \textsf{``}($\text{Id}$)\textsf{''} +axioms used to prove the sequents $S_{3}$ and $S_{6}$. Let us write +the code that serves as the \textsf{``}evidence of proof\textsf{''} for these sequents. +For brevity, we denote $\gamma\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta$ +and $C\triangleq\left(A\rightarrow A\right)\rightarrow B$, so that +$\gamma={\cal CH}(C)$. Then we can write: \begin{align*} - & f_{1}^{A,B,C}:\left(A+B\right)\times C\rightarrow A\times C+B\times C\quad,\\ - & f_{2}^{A,B,C}:A\times C+B\times C\rightarrow\left(A+B\right)\times C\quad. + & S_{3}\triangleq\beta\vdash\beta\quad,\quad\quad\text{Proof}\,(S_{3})_{\text{given }y^{:B}}=y\quad,\\ + & S_{6}\triangleq\gamma,\alpha\vdash\alpha\quad,\quad\quad\text{Proof}\,(S_{6})_{\text{given }q^{:C},x^{:A}}=x\quad. \end{align*} -Since the two logical formulas (\ref{eq:ch-example-distributive-1a})\textendash (\ref{eq:ch-example-distributive-1b}) -are true theorems in constructive logic, we expect to be able to implement -the functions \lstinline!f1! and \lstinline!f2!. It is not straightforward -to guess how to combine the proof rules of Table~\ref{tab:Proof-rules-of-constructive-and-boolean} -to obtain proofs of Eqs.~(\ref{eq:ch-example-distributive-1a})\textendash (\ref{eq:ch-example-distributive-1b}). -So, instead of deriving the implementations of \lstinline!f1! and -\lstinline!f2! from the CH correspondence, we will write the Scala -code directly. +Note that the proof of $S_{6}$ does not use the first given value +$q^{:C}$ (corresponding to the premise $\gamma$). -To implement \lstinline!f1!, we need to perform pattern matching -on the argument: -\begin{lstlisting} -def f1[A, B, C]: ((Either[A, B], C)) => Either[(A, C), (B, C)] = { - case (Left(a), c) => Left((a, c)) // No other choice here. - case (Right(b), c) => Right((b, c)) // No other choice here. -} -\end{lstlisting} -In both cases, we have only one possible expression of the correct -type. - -Similarly, the implementation of \lstinline!f2! leaves us no choices: - -\begin{wrapfigure}{l}{0.65\columnwidth}% -\vspace{-0.5\baselineskip} -\begin{lstlisting} -def f2[A, B, C]: Either[(A, C), (B, C)] => (Either[A, B], C) = { - case Left((a, c)) => (Left(a), c) // No other choice here. - case Right((b, c)) => (Right(b), c) // No other choice here. +We now shorten the proof tree by replacing the sequents $S_{3}$ and +$S_{6}$ by their \textsf{``}evidence of proof\textsf{''}: +\[ +\xymatrix{\xyScaleY{1.5pc}\xyScaleX{4pc} & & & \square\\ +\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[r]\sp(0.5){S_{2}}\ar[ru]\sp(0.6){(y)_{\text{given }y^{:B}}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){(x)_{\text{given }q^{:C},x^{:A}}} & \square } -\end{lstlisting} -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -\noindent The code of \lstinline!f1! and \lstinline!f2! never discards -any given values; in other words, these functions appear to preserve -information. We can formulate this property rigorously as a requirement -that an arbitrary value \lstinline!x: (Either[A, B], C)! be mapped -by \lstinline!f1! to some value \lstinline!y: Either[(A, C), (B, C)]! -and then mapped by \lstinline!f2! back to \emph{the same} value \lstinline!x!. -Similarly, any value \lstinline!y! of type \lstinline!Either[(A, C), (B, C)]! -should be transformed by \lstinline!f2! and then by \lstinline!f1! -back to the same value \lstinline!y!. +\] -Let us write these conditions as equations: +The next step is to consider the proof of $S_{2}$, which is found +by applying the rule \textsf{``}($\text{Right}\Rightarrow$)\textsf{''}. This rule +promises to give a proof of $S_{2}$ if we have a proof of $S_{6}$. +In order to extract code from that rule, we can write a function that +transforms a proof of $S_{6}$ into a proof of $S_{2}$. We call this +function the \textbf{proof transformer}\index{Curry-Howard correspondence!proof transformer}\index{proof transformer} +corresponding to the rule \textsf{``}($\text{Right}\Rightarrow$)\textsf{''}. That +rule and its transformer are defined as: +\begin{align*} +\frac{\Gamma,A\vdash B}{\Gamma\vdash A\Rightarrow B}~(\text{Right}\Rightarrow)\quad: & \quad\quad\text{Proof}\,(\Gamma\vdash A\Rightarrow B)_{\text{given }p^{:\Gamma}}\\ + & \quad\quad=x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash B)_{\text{given }p^{:\Gamma},x^{:A}}\quad. +\end{align*} +Applying the proof transformer to the known proof of $S_{6}$, we +obtain a proof of $S_{2}$: \[ -\forall x^{:(A+B)\times C}.\,f_{2}(f_{1}(x))=x\quad,\quad\quad\forall y^{:A\times C+B\times C}.\,f_{1}\left(f_{2}(y)\right)=y\quad. +\text{Proof}\,(S_{2})_{\text{given }q^{:C}}=x^{:A}\rightarrow\text{Proof}\,(S_{6})_{\text{given }q^{:C},x^{:A}}=(x^{:A}\rightarrow x)_{\text{given }q^{:C}}\quad. +\] +The proof tree can be now shortened to: +\[ +\xymatrix{\xyScaleY{1.5pc}\xyScaleX{3.5pc} & & & \square\\ +\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[rr]\sp(0.62){(x^{:A}\rightarrow x)_{\text{given }q^{:C}}}\ar[ru]\sp(0.6){(y)_{\text{given }y^{:B}}} & & \square +} \] -If these equations hold, it means that all the information in a value -$x^{:(A+B)\times C}$ is completely preserved inside the value $y\triangleq f_{1}(x)$; -the original value $x$ can be recovered as $x=f_{2}(y)$. Then the -function $f_{1}$ is the \textbf{inverse}\index{inverse function} -of $f_{2}$. Conversely, all the information in a value $y^{:A\times C+B\times C}$ -is preserved inside $x\triangleq f_{2}(y)$ and can be recovered by -applying $f_{1}$. Since the values $x^{:(A+B)\times C}$ and $y^{:A\times C+B\times C}$ -are arbitrary, it will follow that the \emph{data types} themselves, -$\left(A+B\right)\times C$ and $A\times C+B\times C$, carry equivalent -information. Such types are called equivalent\index{types!equivalent} -or isomorphic\index{types!isomorphic}\index{isomorphic types}. -Generally, we say that types $P$ and $Q$ are \textbf{equivalent} -or \textbf{isomorphic} (denoted $P\cong Q$) \index{type equivalence}when -there exist functions $f_{1}:P\rightarrow Q$ and $f_{2}:Q\rightarrow P$ -that are inverses of each other. We can write these conditions using -the notation $(f_{1}\bef f_{2})(x)\triangleq f_{2}(f_{1}(x))$ as: +The next step is to get the proof of $S_{1}$ obtained by applying +the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''}. That rule requires two +previous sequents, so its transformer is a function of two previously +obtained proofs: +\begin{align*} + & \frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,A\Rightarrow B\vdash C}~(\text{Left}\Rightarrow)\quad:\\ + & \text{Proof}\,(\Gamma,A\Rightarrow B\vdash C)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p^{:\Gamma},b^{:B}}\\ + & \quad\quad\text{where}\quad b^{:B}\triangleq q\big(\text{Proof}\,(\Gamma,A\Rightarrow B\vdash A)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}\big)\quad. +\end{align*} +In the proof tree shown above, we obtain a proof of $S_{1}$ by applying +this proof transformer to the proofs of $S_{2}$ and $S_{3}$: +\begin{align*} + & \text{Proof}\,(S_{1})_{\text{given }q^{:C}}=\text{Proof}\,(S_{3})_{\text{given }b^{:B}}\text{ where }b^{:B}\triangleq q(\text{Proof}\,(S_{2}))_{\text{given }q^{:C}}\\ + & \quad=b\text{ where }b^{:B}\triangleq q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}=q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}\quad. +\end{align*} +Substituting this proof into the proof tree, we shorten the tree to: \[ -f_{1}\bef f_{2}=\text{id}\quad,\quad\quad f_{2}\bef f_{1}=\text{id}\quad. +\xymatrix{\xyScaleY{1.5pc}\xyScaleX{3.5pc}\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}} & \square} \] -(In Scala, the forward composition $f_{1}\bef f_{2}$ is the function -\lstinline!f1 andThen f2!. We omit type annotations since we already -checked that the types match.) If these conditions hold, there is -a one-to-one correspondence between values of types $P$ and $Q$. -This is the same as to say that the data types $P$ and $Q$ \textsf{``}carry -equivalent information\textsf{''}. -To verify that the Scala functions \lstinline!f1! and \lstinline!f2! -defined above are inverses of each other, we first check if $f_{1}\bef f_{2}=\text{id}$. -Applying $f_{1}\bef f_{2}$ means to apply $f_{1}$ and then to apply -$f_{2}$ to the result. Begin by applying $f_{1}$ to an arbitrary -value $x^{:(A+B)\times C}$. A value $x$ of that type can be in only -one of the two disjoint cases: a tuple \lstinline!(Left(a), c)! or -a tuple \lstinline!(Right(b), c)!, for some values \lstinline!a:A!, -\lstinline!b:B!, and \lstinline!c:C!. The Scala code of \lstinline!f1! -maps these tuples to \lstinline!Left((a, c))! and to \lstinline!Right((b, c))! -respectively; we can see this directly from the code of \lstinline!f1!. -We then apply $f_{2}$ to those values, which maps them back to a -tuple \lstinline!(Left(a), c)! or to a tuple \lstinline!(Right(b), c)! -respectively, according to the code of \lstinline!f2!. These tuples -are exactly the value $x$ we started with. So, applying $f_{1}\bef f_{2}$ -to an arbitrary $x^{:(A+B)\times C}$ returns that value $x$. This -is the same as to say that $f_{1}\bef f_{2}=\text{id}$. +It remains to obtain the proof of $S_{0}$ by applying the proof transformer +of the rule \textsf{``}($\text{Right}\Rightarrow$)\textsf{''}: +\begin{align*} + & \text{Proof}\,(S_{0})=\text{Proof}\,(\emptyset\vdash(\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta)\\ + & =q^{:(A\rightarrow A)\rightarrow B}\rightarrow\text{Proof}\,(S_{1})_{\text{given }q^{:C}}=q^{:(A\rightarrow A)\rightarrow B}\rightarrow q(x^{:A}\rightarrow x)\quad. +\end{align*} +The proof tree is now shortened to the code expression $q^{:(A\rightarrow A)\rightarrow B}\rightarrow q(x^{:A}\rightarrow x)$ +has type $\left(\left(A\rightarrow A\right)\rightarrow B\right)\rightarrow B$. +So, that code is an evidence of proof for $S_{0}$. In this way, we +have derived the code of a fully parametric function from its type +signature. -To check whether $f_{2}\bef f_{1}=\text{id}$, we apply $f_{2}$ to -an arbitrary value $y^{:A\times C+B\times C}$, which must be one -of the two disjoint cases, \lstinline!Left((a, c))! or \lstinline!Right((b, c))!. -The code of \lstinline!f2! maps these two cases into tuples \lstinline!(Left(a), c)! -and \lstinline!(Right(b), c)! respectively. Then we apply \lstinline!f1! -and map these tuples back to \lstinline!Left((a, c))! and \lstinline!Right((b, c))! -respectively. It follows that applying $f_{2}$ and then $f_{1}$ -will always return the initial value. As a formula, this is written -as $f_{2}\bef f_{1}=\text{id}$. +Figure~\ref{fig:proof-transformers-for-LJ-rules} shows the proof +transformers for all the rules of the LJ algorithm. Apart from the +special rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''}, all other rules have +proof transformers using just one of the code constructions (\textsf{``}create +function\textsf{''}, \textsf{``}create tuple\textsf{''}, \textsf{``}use tuple\textsf{''}, etc.) allowed within +fully parametric code. -By looking at the code of \lstinline!f1! and \lstinline!f2!, we -can directly observe that these functions are inverses of each other: -the tuple pattern \lstinline!(Left(a), c)! is mapped to \lstinline!Left((a, c))!, -and the pattern \lstinline!(Right(b), c)! to \lstinline!Right((b, c))!, -or vice versa. It is visually clear that no information is lost and -that the original values are returned by function compositions $f_{1}\bef f_{2}$ -or $f_{2}\bef f_{1}$. +\subsubsection*{The LJT algorithm} -We find that the logical identity~(\ref{eq:ch-example-distributive-1}) -leads to an equivalence of the corresponding types: -\begin{equation} -\left(A+B\right)\times C\cong A\times C+B\times C\quad.\label{eq:ch-distributive-law-types} -\end{equation} -To get Eq.~(\ref{eq:ch-distributive-law-types}) from Eq.~(\ref{eq:ch-example-distributive-1}), -we need to convert a logical formula to an arithmetic expression by -replacing the disjunction operations $\vee$ by $+$ and the conjunctions -$\wedge$ by $\times$ everywhere. +As we have seen, the LJ algorithm enters a loop if the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''} +gives a sequent we already had at a previous step. That rule requires +us to prove two new sequents: +\[ +\frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,A\Rightarrow B\vdash C}~(\text{Left}\Rightarrow)\quad. +\] +A sign of trouble is that the first of these sequents ($\Gamma,A\Rightarrow B\vdash A$) +does not have a simpler form than the initial sequent ($\Gamma,A\Rightarrow B\vdash C$). +So, it is not clear that we are getting closer to completing the proof. +If $A=C$, the new sequent will simply repeat the initial sequent, +immediately creating a loop. -As another example of a logical identity, consider the associativity -law for conjunction: -\begin{equation} -\left(\alpha\wedge\beta\right)\wedge\gamma=\alpha\wedge\left(\beta\wedge\gamma\right)\quad.\label{eq:ch-example-associativity-conjunction} -\end{equation} -The corresponding types are $(A\times B)\times C$ and $A\times(B\times C)$; -in Scala, \lstinline!((A, B), C)! and \lstinline!(A, (B, C))!. We -can define functions that convert between these types without information -loss\index{information loss}: -\begin{lstlisting} -def f3[A, B, C]: (((A, B), C)) => (A, (B, C)) = { case ((a, b), c) => (a, (b, c)) } -def f4[A, B, C]: (A, (B, C)) => (((A, B), C)) = { case (a, (b, c)) => ((a, b), c) } -\end{lstlisting} -By applying these functions to arbitrary values of types \lstinline!((A, B), C)! -and \lstinline!(A, (B, C))!, it is easy to see that the functions -\lstinline!f3! and \lstinline!f4! are inverses of each other. This -is also directly visible in the code: the nested tuple pattern \lstinline!((a, b), c)! -is mapped to the pattern \lstinline!(a, (b, c))! and back. So, the -types $\left(A\times B\right)\times C$ and $A\times\left(B\times C\right)$ -are equivalent, and we can write $A\times B\times C$ without parentheses. +In some cases, a repeated sequent will occur after more than one step. +It is not easy to formulate rigorous conditions for stopping the loop +or for avoiding the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''}. -Does a logical identity always correspond to an equivalence of types? -This turns out to be \emph{not} so. A simple example of a logical -identity that does not correspond to a type equivalence is: -\begin{equation} -True\vee\alpha=True\quad.\label{eq:ch-example-logic-identity-2} -\end{equation} -Since the CH correspondence maps the logical constant $True$ into -the unit type $\bbnum 1$, the type equivalence corresponding to Eq.~(\ref{eq:ch-example-logic-identity-2}) -is $\bbnum 1+A\cong\bbnum 1$. The type denoted by $\bbnum 1+A$ means -\lstinline!Option[A]! in Scala, so the corresponding equivalence -is \lstinline!Option[A]!$\cong$\lstinline!Unit!. Intuitively, this -type equivalence should not hold: an \lstinline!Option[A]! may carry -a value of type \lstinline!A!, which cannot possibly be stored in -a value of type \lstinline!Unit!. We can verify this intuition rigorously -by proving that any fully parametric functions with type signatures -$g_{1}:\bbnum 1+A\rightarrow\bbnum 1$ and $g_{2}:\bbnum 1\rightarrow\bbnum 1+A$ -will not satisfy $g_{1}\bef g_{2}=\text{id}$. To verify this, we -note that $g_{2}:\bbnum 1\rightarrow\bbnum 1+A$ must have this type -signature: +The LJT algorithm solves this problem by removing the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''} +from the LJ algorithm. Instead, \emph{four} new rules are introduced. +Each of these rules contains a different pattern instead of $A$ in +the premise $A\Rightarrow C$: +\begin{align*} +\text{(}A\text{ is atomic)\,}\frac{\Gamma,A,B\vdash D}{\Gamma,A,{\color{blue}A\Rightarrow B}\vdash D}~(\text{Left}\Rightarrow_{A})\qquad & \quad\frac{\Gamma,A\Rightarrow B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\wedge B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\wedge})\\ +\frac{\Gamma,B\Rightarrow C\vdash A\Rightarrow B\quad\quad\Gamma,C\vdash D}{\Gamma,{\color{blue}(A\Rightarrow B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\Rightarrow})\qquad & \quad\frac{\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\vee B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\vee}) +\end{align*} +The rule \textsf{``}$\text{Left}\Rightarrow_{A}$\textsf{''} applies only if the implication +starts with an \textsf{``}atomic\textsf{''} type expression, i.e., a single type parameter +or a unit type. In all other cases, the implication must start with +a conjunction, a disjunction, or an implication, which means that +one of the three remaining rules will apply. -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.8\baselineskip} -\begin{lstlisting} -def g2[A]: Unit => Option[A] = ??? -\end{lstlisting} +The LJT algorithm retains all the rules in Figure~\ref{fig:proof-transformers-for-LJ-rules} +except the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''}, which is replaced +by the four new rules. It is far from obvious that the new rules are +equivalent to the old ones. It took mathematicians several decades +to come up with the LJT rules and to prove their validity. This book +will rely on that result and will not attempt to prove it. -\vspace{-1\baselineskip} -\end{wrapfigure}% +The proof transformers for the new rules are shown in Figure~\ref{fig:proof-transformers-for-LJT-rules}. +Figures~\ref{fig:proof-transformers-for-LJ-rules}\textendash \ref{fig:proof-transformers-for-LJT-rules} +define the set of proof transformers sufficient for using the LJT +algorithm in practice. The \index{curryhoward library@\texttt{curryhoward} library}\lstinline!curryhoward! +library\texttt{}\footnote{See \texttt{\href{https://github.com/Chymyst/curryhoward}{https://github.com/Chymyst/curryhoward}}} +implements these proof transformers. -\noindent This function must always return \lstinline!None!, since -a fully parametric function cannot produce values of an arbitrary -type \lstinline!A! from scratch. Therefore, $g_{1}\bef g_{2}$ is -also a function that always returns \lstinline!None!. The function -$g_{1}\bef g_{2}$ has type signature $\bbnum 1+A\rightarrow\bbnum 1+A$ -or, in Scala syntax, \lstinline!Option[A] => Option[A]!, and is not -equal to the identity function, because the identity function does -\emph{not} always return \lstinline!None!. +The most complicated of the new rules is the rule \textsf{``}($\text{Left}\Rightarrow_{\Rightarrow}$)\textsf{''}. +It is far from obvious why the rule $\text{Left}\Rightarrow_{\Rightarrow}$ +is useful or even correct. This rule is based on a non-trivial logic +identity: +\[ +\left(\left(A\rightarrow B\right)\rightarrow C\right)\rightarrow\left(A\rightarrow B\right)\,\Longleftrightarrow\,\left(B\rightarrow C\right)\rightarrow\left(A\rightarrow B\right)\quad. +\] +Consider the type at the left-hand side of this identity: +\[ +\left(\left(A\rightarrow B\right)\rightarrow C\right)\rightarrow B\rightarrow C\quad. +\] +A function with that type can be written as: +\[ +f=k^{:\left(A\rightarrow B\right)\rightarrow C}\rightarrow b^{:B}\rightarrow k\,(\_^{:A}\rightarrow b)\quad. +\] +The function $f$ occurs in the proof transformer for the rule $\text{Left}\Rightarrow_{\Rightarrow}$ +(shown below in Table~\ref{fig:proof-transformers-for-LJT-rules}). +Note that this $f$ applies $k$ to a function $(\_\rightarrow b)$ +that ignores its argument. We expect to be able to simplify the resulting +expression at the place when $(\_\rightarrow b)$ is applied to some +argument expression, which we can then ignore. For this reason, applying +the transformer for the rule $\text{Left}\Rightarrow_{\Rightarrow}$ +results in evidence-of-proof code that is longer than the code obtained +via LJ\textsf{'}s rule transformers. The code obtained via the LJT algorithm +needs to be simplified symbolically. + +As an example of using the LJT algorithm, we again prove the sequent +from the previous section: $S_{0}=\emptyset\vdash((\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta$. +At each step, only one LJT rule applies to each sequent. The initial +part of the proof tree looks like this:\vspace{-1\baselineskip} +\[ +\xymatrix{\xyScaleY{1.5pc}\xyScaleX{6pc} & & & ~\\ +\ar[r]\sp(0.4){\emptyset\vdash((\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){(\alpha\Rightarrow\alpha)\Rightarrow\beta\vdash\beta} & (\text{Left}\Rightarrow_{\Rightarrow})\ar[ru]\sp(0.65){\beta\vdash\beta}\ar[r]\sp(0.65){\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha} & ~ +} +\] +The proofs for the sequents $\beta\vdash\beta$ and $\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha$ +are the same as before: +\[ +\text{Proof}\,(\beta\vdash\beta)_{\text{given }y^{:B}}=y\quad,\quad\quad\text{Proof}\,(\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha)_{\text{given }r^{:A\rightarrow B}}=x^{:A}\rightarrow x\quad. +\] +Substituting these proofs into the proof transformer of the rule \textsf{``}($\text{Left}\Rightarrow_{\Rightarrow}$)\textsf{''} +produces this code: +\begin{align*} + & \text{Proof}\,((\alpha\Rightarrow\alpha)\Rightarrow\beta\vdash\beta)_{\text{given }q^{:(A\rightarrow A)\rightarrow B}}=q\big(\text{Proof}\,(\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha)_{\text{given }r^{:A\rightarrow B}}\big)\\ + & \quad\quad\text{where }r^{:A\rightarrow B}=a^{:A}\rightarrow q(\_^{:A}\rightarrow a)\\ + & =q(x^{:A}\rightarrow x)\quad. +\end{align*} +The proof of $\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha$ +does not actually use the intermediate value $r^{:A\rightarrow B}$ +provided by the proof transformer. As a symbolic simplification step, +we may simply omit the code of $r$. The \lstinline!curryhoward! +library always performs symbolic simplification after applying the +LJT algorithm. -Another example of a logical identity without a type equivalence is -the distributive law: -\begin{equation} -\forall(A,B,C).\,\left(A\wedge B\right)\vee C=\left(A\vee C\right)\wedge\left(B\vee C\right)\quad,\label{eq:ch-example-distributive-2} -\end{equation} -which is \textsf{``}dual\textsf{''} to the law~(\ref{eq:ch-example-distributive-1}), -i.e., it is obtained from Eq.~(\ref{eq:ch-example-distributive-1}) -by swapping all conjunctions ($\wedge$) with disjunctions ($\vee$). -In logic, a dual formula to an identity is also an identity. The CH -correspondence maps Eq.~(\ref{eq:ch-example-distributive-2}) into -the type equation: +\begin{figure} +\begin{centering} +{\small{}}% +\noindent\fbox{\begin{minipage}[t]{1\columnwidth - 2\fboxsep - 2\fboxrule}% +{\small{} +\begin{align*} +\frac{\Gamma,A,B\vdash D}{\Gamma,A,{\color{blue}A\Rightarrow B}\vdash D}~(\text{Left}\Rightarrow_{A})\quad & \quad\text{Proof}\,(\Gamma,A,A\Rightarrow B\vdash D)_{\text{given }p^{:\Gamma},x^{:A},q^{:A\rightarrow B}}\\ + & \quad\quad=\text{Proof}\,(\Gamma,A,B\vdash D)_{\text{given }p,x,q(x)}\\ +\frac{\Gamma,A\Rightarrow B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\wedge B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\wedge})\quad & \quad\text{Proof}\,(\Gamma,(A\wedge B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:A\times B\rightarrow C}}\\ + & \quad\quad=\text{Proof}\,(\Gamma,\\ + & \quad\quad\quad A\Rightarrow B\Rightarrow C\vdash D)_{\text{given }p,(a^{:A}\rightarrow b^{:B}\rightarrow q(a\times b))}\\ +\frac{\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\vee B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\vee})\quad & \quad\text{Proof}\,(\Gamma,(A\vee B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:A+B\rightarrow C}}\\ + & \quad\quad=\text{Proof}\,(\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D)_{\text{given }p,r,s}\\ + & \quad\quad\text{where}~r\triangleq a^{:A}\rightarrow q(a+\bbnum 0)\\ + & \quad\quad\text{ and }s\triangleq b^{:B}\rightarrow q(\bbnum 0+b)\\ +\frac{\Gamma,B\Rightarrow C\vdash A\Rightarrow B\quad\Gamma,C\vdash D}{\Gamma,{\color{blue}(A\Rightarrow B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\Rightarrow})\quad & \quad\text{Proof}\,(\Gamma,(A\Rightarrow B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:\left(A\rightarrow B\right)\rightarrow C}}\\ + & \quad\quad=\text{Proof}\,(\Gamma,C\vdash D)_{\text{given }p,c}\\ + & \quad\quad\text{ where}~c^{:C}\triangleq q\big(\text{Proof}\,(\Gamma,\\ + & \quad\quad\quad\quad\quad\quad\quad\quad B\Rightarrow C\vdash A\Rightarrow B)_{\text{given }p,r}\big)\\ + & \quad\quad\text{ and }r^{:B\rightarrow C}\triangleq b^{:B}\rightarrow q(\_^{:A}\rightarrow b) +\end{align*} +}% +\end{minipage}}{\small\par} +\par\end{centering} +\caption{\label{fig:proof-transformers-for-LJT-rules}Proof transformers for +the four new rules of the \index{LJT algorithm|textit}LJT algorithm.} +\end{figure} + +The reason the LJT algorithm terminates is that each rule replaces +a given sequent by one or more sequents with simpler premises or goals.\footnote{The paper \texttt{\href{http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.35.2618}{http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.35.2618}} +shows that the LJT algorithm terminates by giving an explicit decreasing +measure on proof trees.} This guarantees that the proof search will terminate either with +a complete proof or with a sequent to which no more rules apply. An +example of such a \textsf{``}dead-end\textsf{''} sequent is $\alpha\vdash\beta$ where +$\alpha$ and $\beta$ are different, unrelated propositions. When +no more rules apply, the LJT algorithm concludes that the initial +sequent cannot be proved. + +To \emph{prove} that there is no proof, one needs to use methods +that are beyond the scope of this book. An introduction to the required +techniques is in the book \textsf{``}Proof and Disproof in Formal Logic\textsf{''} +by R.~Bornat\index{Richard Bornat} (see footnote~\ref{fn:Bornat-proof-book} +on page~\pageref{fn:Bornat-proof-book}). + +\subsection{Failure of Boolean logic in reasoning about $\mathcal{CH}$-propositions\label{subsec:Example:-Failure-of-Boolean-logic}} + +Programmers are familiar with the \index{Boolean logic}Boolean logic +whose operations are written in Scala as \lstinline!x && y! (conjunction), +\lstinline!x || y! (disjunction), and \lstinline*!x* (negation). +However, it turns out that the Boolean logic does \emph{not} always +produce correct conclusions when reasoning about ${\cal CH}$-propositions. +One needs to use the constructive logic to reason correctly about +implementable type signatures. + +Let us nevertheless briefly look at how Boolean logic would handle +that reasoning. In the Boolean logic, each proposition ($\alpha$, +$\beta$, ...) is either $True$ or $False$. The operations are $\alpha\wedge\beta$ +(conjunction), $\alpha\vee\beta$ (disjunction), and $\neg\alpha$ +(negation). The \textbf{implication} \index{implication (in logic)} +($\Rightarrow$) is defined through other operations by: \begin{equation} -\forall(A,B,C).\,\left(A\times B\right)+C=\left(A+C\right)\times\left(B+C\right)\quad.\label{eq:ch-example-incorrect-identity-2} +\left(\alpha\Rightarrow\beta\right)\triangleq\left((\neg\alpha)\vee\beta\right)\quad.\label{eq:ch-definition-of-implication-in-Boolean-logic} \end{equation} -However, the types $A\times B+C$ and $\left(A+C\right)\times\left(B+C\right)$ -are \emph{not} equivalent. To see why, look at the possible code of -a function $g_{3}:\left(A+C\right)\times\left(B+C\right)\rightarrow A\times B+C$: -\begin{lstlisting}[numbers=left] -def g3[A,B,C]: ((Either[A, C], Either[B, C])) => Either[(A, B), C] = { - case (Left(a), Left(b)) => Left((a, b)) // No other choice. - case (Left(a), Right(c)) => Right(c) // No other choice. - case (Right(c), Left(b)) => Right(c) // No other choice. - case (Right(c1), Right(c2)) => Right(c1) // Must discard c1 or c2 here! -} // May return Right(c2) instead of Right(c1) in the last line. -\end{lstlisting} -In line 5, we have a choice of returning \lstinline!Right(c1)! or -\lstinline!Right(c2)!. Whichever we choose, we will lose information\index{information loss} -because we will have discarded one of the given values \lstinline!c1!, -\lstinline!c2!. After evaluating $g_{3}$, we will not be able to -restore \emph{both} \lstinline!c1! and \lstinline!c2!, no matter -what code we write. So, the composition $g_{3}\bef g_{4}$ with any -$g_{4}$ cannot be equal to the identity function. The type equation~(\ref{eq:ch-example-incorrect-identity-2}) -is incorrect. - -We find that a logical identity ${\cal CH}(P)={\cal CH}(Q)$ guarantees, -via the CH correspondence, that we can implement \emph{some} fully -parametric functions of types $P\rightarrow Q$ and $Q\rightarrow P$. -However, it is not guaranteed that these functions are inverses of -each other, i.e., that the type conversions $P\rightarrow Q$ or $Q\rightarrow P$ -have no information loss\index{information loss}. So, the type equivalence -$P\cong Q$ does not automatically follow from the logical identity -${\cal CH}(P)={\cal CH}(Q)$. -The CH correspondence means that for true propositions ${\cal CH}(X)$ -we can compute \emph{some} value $x$ of type $X$. However, the CH -correspondence does not guarantee that the computed value $x^{:X}$ -will satisfy any additional properties or laws. +To verify whether a formula is true in the Boolean logic, we can substitute +either $True$ or $False$ into every variable and check if the formula +has the value $True$ in all possible cases. The result can be arranged +into a \index{truth table}truth table. The formula is true if all +values in its truth table are $True$. -\subsection{Arithmetic identity corresponds to type equivalence} +Disjunction, conjunction, negation, and implication operations have +the following truth table: +\begin{center} +{\small{}}% +\begin{tabular}{|c|c|c|c|c|c|} +\hline +{\small{}$\alpha$} & {\small{}$\beta$} & \textbf{\small{}$\alpha\vee\beta$} & \textbf{\small{}$\alpha\wedge\beta$} & \textbf{\small{}$\neg\alpha$} & \textbf{\small{}$\alpha\Rightarrow\beta$}\tabularnewline +\hline +\hline +{\small{}$True$} & {\small{}$True$} & {\small{}$True$} & {\small{}$True$} & {\small{}$False$} & {\small{}$True$}\tabularnewline +\hline +{\small{}$True$} & {\small{}$False$} & {\small{}$True$} & {\small{}$False$} & {\small{}$False$} & {\small{}$False$}\tabularnewline +\hline +{\small{}$False$} & {\small{}$True$} & {\small{}$True$} & {\small{}$False$} & {\small{}$True$} & {\small{}$True$}\tabularnewline +\hline +{\small{}$False$} & {\small{}$False$} & {\small{}$False$} & {\small{}$False$} & {\small{}$True$} & {\small{}$True$}\tabularnewline +\hline +\end{tabular}{\small\par} +\par\end{center} -Looking at the examples of equivalent types, we notice that correct -type equivalences correspond to \emph{arithmetical} identities rather -than \emph{logical} identities. For instance, the logical identity -in Eq.~(\ref{eq:ch-example-distributive-1}) leads to the type equivalence~(\ref{eq:ch-distributive-law-types}), -which looks like a standard identity of arithmetic, such as: -\[ -(1+10)\times20=1\times20+10\times20\quad. -\] -The logical identity in Eq.~(\ref{eq:ch-example-distributive-2}), -which does \emph{not} yield a type equivalence, leads to an incorrect -arithmetic equation~\ref{eq:ch-example-incorrect-identity-2}, e.g., -$\left(1\times10\right)+20\neq\left(1+20\right)\times\left(10+20\right)$. -Similarly, the associativity law~(\ref{eq:ch-example-associativity-conjunction}) -leads to a type equivalence and to the arithmetic identity: -\[ -\left(a\times b\right)\times c=a\times\left(b\times c\right)\quad, -\] -The logical identity in Eq.~(\ref{eq:ch-example-logic-identity-2}), -which does not yield a type equivalence, leads to an incorrect arithmetic -statement ($\forall a.\,1+a=1$). +Using this table, we find that the formula $\alpha\Rightarrow\alpha$ +has the value $True$ in all cases, whether $\alpha$ itself is $True$ +or $False$. This check is sufficient to show that $\forall\alpha.\,\alpha\Rightarrow\alpha$ +is true in Boolean logic. -Table~\ref{tab:Logical-identities-with-disjunction-and-conjunction} -summarizes these and other examples of logical identities and the -corresponding type equivalences. In all rows, quantifiers such as -$\forall\alpha$ or $\forall(A,B)$ are implied as necessary. +Here is the truth table for the formulas $\forall(\alpha,\beta).\,(\alpha\wedge\beta)\Rightarrow\alpha$ +and $\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\wedge\beta)$. +The first formula is true since all values in its column are $True$, +while the second formula is not true since one value in the last column +is $False$: +\begin{center} +{\small{}}% +\begin{tabular}{|c|c|c|c|c|} +\hline +{\small{}$\alpha$} & {\small{}$\beta$} & \textbf{\small{}$\alpha\wedge\beta$} & {\small{}$(\alpha\wedge\beta)\Rightarrow\alpha$} & {\small{}$\alpha\Rightarrow(\alpha\wedge\beta)$}\tabularnewline +\hline +\hline +{\small{}$True$} & {\small{}$True$} & {\small{}$True$} & {\small{}$True$} & {\small{}$True$}\tabularnewline +\hline +{\small{}$True$} & {\small{}$False$} & {\small{}$False$} & {\small{}$True$} & {\small{}$False$}\tabularnewline +\hline +{\small{}$False$} & {\small{}$True$} & {\small{}$False$} & {\small{}$True$} & {\small{}$True$}\tabularnewline +\hline +{\small{}$False$} & {\small{}$False$} & {\small{}$False$} & {\small{}$True$} & {\small{}$True$}\tabularnewline +\hline +\end{tabular}{\small\par} +\par\end{center} -Because we chose the type notation to be similar to the ordinary arithmetic -notation, it is easy to translate a possible type equivalence into -an arithmetic equation. In all cases, valid arithmetic identities -correspond to type equivalences, and failures to obtain a type equivalence -correspond to incorrect arithmetic identities. With regard to type -equivalence, types such as $A+B$ and $A\times B$ behave similarly -to arithmetic expressions such as $10+20$ and $10\times20$ and not -similarly to logical formulas such as $\alpha\vee\beta$ and $\alpha\wedge\beta$. +Table~\ref{tab:Logical-formulas-Boolean-theorems} shows more examples +of logical formulas that are true in Boolean logic. Each formula is +first written in terms of ${\cal CH}$-propositions (we denote $\alpha\triangleq{\cal CH}(A)$ +and $\beta\triangleq{\cal CH}(B)$ for brevity) and then as a Scala +type signature of a function. So, all these type signatures \emph{can} +be implemented. -\begin{table} +\begin{table}[h] \begin{centering} -\begin{tabular}{|c|c|} +\begin{tabular}{|c|c|c|} \hline -\textbf{\small{}Logical identity} & \textbf{\small{}Type equivalence (if it holds)}\tabularnewline +\textbf{\small{}Logic formula} & \textbf{\small{}Type formula} & \textbf{\small{}Scala code}\tabularnewline \hline \hline -{\small{}$True\vee\alpha=True$} & {\small{}$\bbnum 1+A\not\cong\bbnum 1$}\tabularnewline +{\footnotesize{}$\forall\alpha.\,\alpha\Rightarrow\alpha$} & {\footnotesize{}$\forall A.\,A\rightarrow A$} & \lstinline!def id[A](x: A): A = x!\tabularnewline \hline -{\small{}$True\wedge\alpha=\alpha$} & {\small{}$\bbnum 1\times A\cong A$}\tabularnewline +{\footnotesize{}$\forall\alpha.\,\alpha\Rightarrow True$} & {\footnotesize{}$\forall A.\,A\rightarrow\bbnum 1$} & \lstinline!def toUnit[A](x: A): Unit = ()!\tabularnewline \hline -{\small{}$False\vee\alpha=\alpha$} & {\small{}$\bbnum 0+A\cong A$}\tabularnewline +{\footnotesize{}$\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\vee\beta)$} & {\footnotesize{}$\forall(A,B).\,A\rightarrow A+B$} & \lstinline!def f[A, B](x: A): Either[A, B] = Left(x)!\tabularnewline \hline -{\small{}$False\wedge\alpha=False$} & {\small{}$\bbnum 0\times A\cong\bbnum 0$}\tabularnewline +{\footnotesize{}$\forall(\alpha,\beta).\,(\alpha\wedge\beta)\Rightarrow\alpha$} & {\footnotesize{}$\forall(A,B).\,A\times B\rightarrow A$} & \lstinline!def f[A, B](p: (A, B)): A = p._1!\tabularnewline \hline -{\small{}$\alpha\vee\beta=\beta\vee\alpha$} & {\small{}$A+B\cong B+A$}\tabularnewline +{\footnotesize{}$\forall(\alpha,\beta).\,\alpha\Rightarrow(\beta\Rightarrow\alpha)$} & {\footnotesize{}$\forall(A,B).\,A\rightarrow(B\rightarrow A)$} & \lstinline!def f[A, B](x: A): B => A = (_ => x)!\tabularnewline \hline -{\small{}$\alpha\wedge\beta=\beta\wedge\alpha$} & {\small{}$A\times B\cong B\times A$}\tabularnewline +\end{tabular} +\par\end{centering} +\caption{Examples of logical formulas that are true theorems in Boolean logic.\label{tab:Logical-formulas-Boolean-theorems}} +\end{table} + +Table~\ref{tab:Logical-formulas-not-Boolean-theorems} shows some +examples of formulas that are \emph{not true} in Boolean logic. Translated +into type formulas and then into Scala, these formulas yield type +signatures that \emph{cannot} be implemented by fully parametric functions. + +\begin{table}[h] +\begin{centering} +\begin{tabular}{|c|c|c|} \hline -{\small{}$\left(\alpha\vee\beta\right)\vee\gamma=\alpha\vee\left(\beta\vee\gamma\right)$} & {\small{}$\left(A+B\right)+C\cong A+\left(B+C\right)$}\tabularnewline +\textbf{\small{}Logic formula} & \textbf{\small{}Type formula} & \textbf{\small{}Scala type signature}\tabularnewline \hline -{\small{}$\left(\alpha\wedge\beta\right)\wedge\gamma=\alpha\wedge\left(\beta\wedge\gamma\right)$} & {\small{}$\left(A\times B\right)\times C\cong A\times\left(B\times C\right)$}\tabularnewline \hline -{\small{}$\left(\alpha\vee\beta\right)\wedge\gamma=\left(\alpha\wedge\gamma\right)\vee\left(\beta\wedge\gamma\right)$} & {\small{}$\left(A+B\right)\times C\cong A\times C+B\times C$}\tabularnewline +{\footnotesize{}$\forall\alpha.\,True\Rightarrow\alpha$} & {\footnotesize{}$\forall A.\,\bbnum 1\rightarrow A$} & \lstinline!def f[A](x: Unit): A!\tabularnewline \hline -{\small{}$\left(\alpha\wedge\beta\right)\vee\gamma=\left(\alpha\vee\gamma\right)\wedge\left(\beta\vee\gamma\right)$} & {\small{}$\left(A\times B\right)+C\not\cong\left(A+C\right)\times\left(B+C\right)$}\tabularnewline +{\footnotesize{}$\forall(\alpha,\beta).\,(\alpha\vee\beta)\Rightarrow\alpha$} & {\footnotesize{}$\forall(A,B).\,A+B\rightarrow A$} & \lstinline!def f[A,B](x: Either[A, B]): A!\tabularnewline +\hline +{\footnotesize{}$\forall(\alpha,\beta).\,\alpha\Rightarrow(\alpha\wedge\beta)$} & {\footnotesize{}$\forall(A,B).\,A\rightarrow A\times B$} & \lstinline!def f[A,B](p: A): (A, B)!\tabularnewline +\hline +{\footnotesize{}$\forall(\alpha,\beta).\,(\alpha\Rightarrow\beta)\Rightarrow\alpha$} & {\footnotesize{}$\forall(A,B).\,(A\rightarrow B)\rightarrow A$} & \lstinline!def f[A,B](x: A => B): A!\tabularnewline \hline \end{tabular} \par\end{centering} -\caption{Logic identities with disjunction and conjunction, and the possible -type equivalences.\label{tab:Logical-identities-with-disjunction-and-conjunction}} +\caption{Examples of logical formulas that are \emph{not} true in Boolean logic.\label{tab:Logical-formulas-not-Boolean-theorems}} \end{table} -We already verified the first line and the last three lines of Table~\ref{tab:Logical-identities-with-disjunction-and-conjunction}. -Other identities are verified in a similar way. Let us begin with -lines 3 and 4 of Table~\ref{tab:Logical-identities-with-disjunction-and-conjunction}, -which involve the proposition $False$ and the corresponding \index{void type}void -type $\bbnum 0$ (Scala\textsf{'}s \lstinline!Nothing!). Reasoning about the -void type needs a special technique that we will now develop while -verifying the type isomorphisms $\bbnum 0\times A\cong\bbnum 0$ and -$\bbnum 0+A\cong A$. - -\subsubsection{Example \label{subsec:ch-Example-0-times-A}\ref{subsec:ch-Example-0-times-A}\index{solved examples}} +At first sight, it may appear from these examples that whenever a +logical formula is true in Boolean logic, the corresponding type signature +can be implemented in code, and vice versa. However, this is \emph{incorrect}: +the rules of Boolean logic are not fully suitable for reasoning about +types in a functional language. We will now show some examples of +formulas that are true in Boolean logic but correspond to unimplementable +type signatures. -Verify the type equivalence $\bbnum 0\times A\cong\bbnum 0$. +The first example is given by the following type: +\begin{equation} +\forall(A,B,C).\,\left(A\rightarrow B+C\right)\rightarrow\left(A\rightarrow B\right)+\left(A\rightarrow C\right)\quad,\label{eq:ch-example-boolean-bad-type} +\end{equation} +which corresponds to the Scala type signature (shown in Section~\ref{subsec:Motivation-and-outlook}): +\begin{lstlisting} +def bad2[A, B, C](g: A => Either[B, C]): Either[A => B, A => C] = ??? +\end{lstlisting} +The function \lstinline!bad2! \emph{cannot} be implemented via fully +parametric code. To see why, consider that the only available data +is a function $g^{:A\rightarrow B+C}$, which returns values of type +$B$ or $C$ depending (in some unknown way) on the input value of +type $A$. The function \lstinline!bad2! must return either a function +of type $A\rightarrow B$ or a function of type $A\rightarrow C$. +How can the code of \lstinline!bad2! make that decision? The only +input data is the function $g$ that takes an argument of type $A$. +We could imagine applying $g$ to various arguments of type $A$ and +to see whether $g$ returns a $B$ or a $C$. However, the type $A$ +is arbitrary, and a fully parametric function cannot produce a value +of type $A$ in order to apply $g$ to it. So, the decision about +whether to return $A\rightarrow B$ or $A\rightarrow C$ must be independent +of $g$. That decision must be hard-coded in the function \lstinline!bad2!. -\subparagraph{Solution} +Suppose we hard-coded the decision to return a function of type $A\rightarrow B$. +How would we create a function of type $A\rightarrow B$ in the body +of \lstinline!bad2!? Given a value $x^{:A}$ of type $A$, we would +need to compute some value of type $B$. Since the type $B$ is arbitrary +(it is a type parameter), we cannot produce a value of type $B$ from +scratch. The only potential source of values of type $B$ is the given +function $g$. The only way of using $g$ is to apply it to $x^{:A}$. +However, for some $x$, the value $g(x)$ may be of the form \lstinline!Right(c)!, +where \lstinline!c! is of type $C$. In that case, we will have a +value of type $C$, not $B$. So, in general, we cannot guarantee +that we can always obtain a value of type $B$ from a given value +$x^{:A}$. This means we cannot build a function of type $A\rightarrow B$ +out of the function $g$. Similarly, we cannot build a function of +type $A\rightarrow C$ out of $g$. -Recall that the type notation $\bbnum 0\times A$ represents the Scala -tuple type \lstinline!(Nothing, A)!. To demonstrate that the type -\lstinline!(Nothing, A)! is equivalent to the type \lstinline!Nothing!, -we need to show that the type \lstinline!(Nothing, A)! has \emph{no} -values. Indeed, how could we create a value of type, say, \lstinline!(Nothing, Int)!? -We would need to fill \emph{both} parts of the tuple. We have values -of type \lstinline!Int!, but we can never get a value of type \lstinline!Nothing!. -So, regardless of the type \lstinline!A!, it is impossible to create -any values of type \lstinline!(Nothing, A)!. In other words, the -set of values of the type \lstinline!(Nothing, A)! is empty. But -that is the definition of the void type \lstinline!Nothing!. The -types \lstinline!(Nothing, A)! (denoted by $\bbnum 0\times A$) and -\lstinline!Nothing! (denoted by $\bbnum 0$) are both void and therefore -equivalent. - -\subsubsection{Example \label{subsec:ch-Example-0-plus-A}\ref{subsec:ch-Example-0-plus-A}} - -Verify the type equivalence $\bbnum 0+A\cong A$. - -\subparagraph{Solution} - -Recall that the type notation $\bbnum 0+A$ represents the Scala type -\lstinline!Either[Nothing, A]!. We need to show that any value of -that type can be mapped without loss of information to a value of -type \lstinline!A!, and vice versa. This means implementing functions -$f_{1}:\bbnum 0+A\rightarrow A$ and $f_{2}:A\rightarrow\bbnum 0+A$ -such that $f_{1}\bef f_{2}=\text{id}$ and $f_{2}\bef f_{1}=\text{id}$. +Whether we decide to return $A\rightarrow B$ or $A\rightarrow C$, +we will not be able to return a value of the required type, as we +just saw. We must conclude that we cannot implement \lstinline!bad! +as a fully parametric function. -The argument of $f_{1}$ is of type \lstinline!Either[Nothing, A]!. -How can we create a value of that type? Our only choices are to create -a \lstinline!Left(x)! with \lstinline!x:Nothing!, or to create a -\lstinline!Right(y)! with \lstinline!y:A!. However, we cannot create -a value \lstinline!x! of type \lstinline!Nothing! because the type -\lstinline!Nothing! has \emph{no} values. We cannot create a \lstinline!Left(x)!. -The only remaining possibility is to create a \lstinline!Right(y)! -with some value \lstinline!y! of type \lstinline!A!. So, any values -of type $\bbnum 0+A$ must be of the form \lstinline!Right(y)!, and -we can extract that \lstinline!y! to obtain a value of type \lstinline!A!: +We could try to switch between $A\rightarrow B$ and $A\rightarrow C$ +depending on a given value of type $A$. This idea, however, means +that we are working with a different type signature: +\[ +\forall(A,B,C).\,\left(A\rightarrow B+C\right)\rightarrow A\rightarrow\left(A\rightarrow B\right)+\left(A\rightarrow C\right)\quad. +\] +This type signature \emph{can} be implemented, for instance, by this +Scala code: \begin{lstlisting} -def f1[A]: Either[Nothing, A] => A = { - case Right(y) => y - // No need for `case Left(x) => ...` since no `x` can ever be given as `Left(x)`. +def q[A, B, C](g: A => Either[B, C]): A => Either[A => B, A => C] = { a => + g(a) match { + case Left(b) => Left(_ => b) + case Right(c) => Right(_ => c) + } } \end{lstlisting} -For the same reason, there is only one implementation of the function -\lstinline!f2!: -\begin{lstlisting} -def f2[A]: A => Either[Nothing, A] = { y => Right(y) } -\end{lstlisting} -It is clear from the code that the functions \lstinline!f1! and \lstinline!f2! -are inverses of each other. +But this is not the required type signature~(\ref{eq:ch-example-boolean-bad-type}). -We have just seen that a value of type $\bbnum 0+A$ is always a \lstinline!Right(y)! -with some \lstinline!y:A!. Similarly, a value of type $A+\bbnum 0$ -is always a \lstinline!Left(x)! with some \lstinline!x:A!. So, we -will use the notation $A+\bbnum 0$ and $\bbnum 0+A$ to \emph{denote} -the \lstinline!Left! and the \lstinline!Right! parts of the disjunctive -type \lstinline!Either!. This notation agrees with the behavior of -the Scala compiler, which will infer the types \lstinline!Either[A, Nothing] !or -\lstinline!Either[Nothing, A]! for these parts: +Now let us convert the type signature~(\ref{eq:ch-example-boolean-bad-type}) +into a ${\cal CH}$-proposition: +\begin{align} + & \forall(\alpha,\beta,\gamma).\,\left(\alpha\Rightarrow\left(\beta\vee\gamma\right)\right)\Rightarrow\left(\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)\right)\quad,\label{eq:abc-example-classical-logic-bad}\\ +\text{where we denoted}\quad & \alpha\triangleq{\cal CH}(A),\quad\beta\triangleq{\cal CH}(B),\quad\gamma\triangleq{\cal CH}(C)\quad.\nonumber +\end{align} +It turns out that this formula is true in Boolean logic. To prove +this, we need to show that Eq.~(\ref{eq:abc-example-classical-logic-bad}) +is equal to $True$ for any Boolean values of the variables $\alpha$, +$\beta$, $\gamma$. One way is to rewrite the expression~(\ref{eq:abc-example-classical-logic-bad}) +using the rules of Boolean logic, such as Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic}): +\begin{align*} + & \gunderline{\alpha\Rightarrow}\left(\beta\vee\gamma\right)\\ +{\color{greenunder}\text{definition of }\Rightarrow\text{ via Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & \quad=(\neg\alpha)\vee\beta\vee\gamma\quad,\\ + & \gunderline{\left(\alpha\Rightarrow\beta\right)}\vee\gunderline{\left(\alpha\Rightarrow\gamma\right)}\\ +{\color{greenunder}\text{definition of }\Rightarrow\text{ via Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & \quad=\gunderline{(\neg\alpha)}\vee\beta\vee\gunderline{(\neg\alpha)}\vee\gamma\\ +{\color{greenunder}\text{property }x\vee x=x\text{ in Boolean logic}:}\quad & \quad=(\neg\alpha)\vee\beta\vee\gamma\quad, +\end{align*} +showing that $\alpha\Rightarrow(\beta\vee\gamma)$ is in fact \emph{equal} +to $\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)$ +in Boolean logic. -\begin{wrapfigure}{l}{0.53\columnwidth}% -\vspace{-0.6\baselineskip} -\begin{lstlisting} -def toLeft[A, B]: A => Either[A, B] = x => Left(x) -def toRight[A, B]: B => Either[A, B] = y => Right(y) +Let us also give a proof via truth-value reasoning. The only possibility +for an implication $X\Rightarrow Y$ to be $False$ is when $X=True$ +and $Y=False$. So, Eq.~(\ref{eq:abc-example-classical-logic-bad}) +can be $False$ only if $\left(\alpha\Rightarrow(\beta\vee\gamma)\right)=True$ +and $\left(\alpha\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\gamma\right)=False$. +A disjunction can be false only when both parts are false; so we must +have both $\left(\alpha\Rightarrow\beta\right)=False$ and $\left(\alpha\Rightarrow\gamma\right)=False$. +This is only possible if $\alpha=True$ and $\beta=\gamma=False$. +But, with these value assignments, we find $\left(\alpha\Rightarrow(\beta\vee\gamma)\right)=False$ +rather than $True$ as we assumed. It follows that we cannot ever +make Eq.~(\ref{eq:abc-example-classical-logic-bad}) equal to $False$. +So, Eq.~(\ref{eq:abc-example-classical-logic-bad}) is true in Boolean +logic. -scala> toLeft(123) -res0: Either[Int, Nothing] = Left(123) +\section{Equivalence of types} -scala> toRight("abc") -res1: Either[Nothing, String] = Right("abc") +We found a correspondence between types, code, logical propositions, +and proofs, which is known as the \textbf{Curry-Howard correspondence}\index{Curry-Howard correspondence}. +An example of the CH correspondence is that a proof of the logical +proposition: +\begin{equation} +\forall(\alpha,\beta).\,\alpha\Rightarrow\left(\beta\Rightarrow\alpha\right)\label{eq:ch-proposition-example-2} +\end{equation} +corresponds to the code of the following function: +\begin{lstlisting} +def f[A, B]: A => (B => A) = { x => _ => x } \end{lstlisting} +With the CH correspondence in mind, we may say that the \emph{existence} +of the code \lstinline!x => _ => x! with the type $A\rightarrow(B\rightarrow A)$ +\textsf{``}is\textsf{''} a proof of the logical formula~(\ref{eq:ch-proposition-example-2}), +because it shows how to compute a value of type $\forall(A,B).\,A\rightarrow B\rightarrow A$. -\vspace{-2\baselineskip} -\end{wrapfigure}% - -\noindent We can write the functions \lstinline!toLeft! and \lstinline!toRight! -in a code notation as: -\begin{align*} - & \text{toLeft}^{A,B}\triangleq x^{:A}\rightarrow x+\bbnum 0^{:B}\quad,\\ - & \text{toRight}^{A,B}\triangleq y^{:B}\rightarrow\bbnum 0^{:A}+y\quad. -\end{align*} -In this notation, a value of the disjunctive type is shown without -using Scala class names such as \lstinline!Either!, \lstinline!Right!, -and \lstinline!Left!. This shortens the writing and speeds up code -reasoning. - -The type annotation $\bbnum 0^{:A}$ is helpful to remind ourselves -about the type parameter $A$ used, e.g., by the disjunctive value -$\bbnum 0^{:A}+y^{:B}$ in the body of \lstinline!toRight[A, B]!. -Without this type annotation, $\bbnum 0+y^{:B}$ means a value of -type \lstinline!Either[A, B]! where the parameter $A$ should be -determined by matching the types of other expressions. When it is -clear what types are being used, we may omit type annotations and -write simply $\bbnum 0+y$ instead of $\bbnum 0^{:A}+y^{:B}$. - -In the notation $\bbnum 0+y^{:B}$, we use the symbol $\bbnum 0$ -rather than an ordinary zero ($0$), to avoid suggesting that $0$ -is a value of type $\bbnum 0$. The void type $\bbnum 0$ has \emph{no} -values, unlike the \lstinline!Unit! type, $\bbnum 1$, which has -a value denoted by $1$ in the code notation. +The Curry-Howard correspondence maps logic formulas such as $(\alpha\vee\beta)\wedge\gamma$ +into type expressions such as $\left(A+B\right)\times C$. We have +seen that types behave similarly to logic formulas in one respect: +A logic formula is a true theorem of constructive logic when the corresponding +type signature can be implemented as a fully parametric function, +and vice versa. -\subsubsection{Example \label{subsec:ch-Example-1xA}\ref{subsec:ch-Example-1xA}} +It turns out that the similarity ends here. In other respects, type +expressions behave as \emph{arithmetic} expressions and not as logic +formulas. For this reason, the type notation used in this book denotes +disjunctive types by $A+B$ and tuples by $A\times B$, which is designed +to remind us of arithmetic expressions (such as $1+2$ and $2\times3$) +rather than of logical formulas (such as $A\vee B$ and $A\wedge B$). -Verify the type equivalence $A\times\bbnum 1\cong A$. +An important use of the type notation is for writing equations with +types. Can we use the arithmetic intuition for writing type equations +such as: +\begin{equation} +\left(A+B\right)\times C=A\times C+B\times C\quad?\label{eq:ch-example-distributive} +\end{equation} +In this section, we will learn how to check whether one type expression +is equivalent to another. -\subparagraph{Solution} +\subsection{Logical identity does not correspond to type equivalence\label{subsec:Logical-identity-not-type-equivalence}} -The corresponding Scala types are the tuple \lstinline!(A, Unit)! -and the type \lstinline!A!. We need to implement functions $f_{1}:\forall A.\,A\times\bbnum 1\rightarrow A$ -and $f_{2}:\forall A.\,A\rightarrow A\times\bbnum 1$ and to demonstrate -that they are inverses of each other. The Scala code for these functions -is: -\begin{lstlisting} -def f1[A]: ((A, Unit)) => A = { case (a, ()) => a } -def f2[A]: A => (A, Unit) = { a => (a, ()) } -\end{lstlisting} -Let us first write a proof by reasoning directly with Scala code: +The CH correspondence maps Eq.~(\ref{eq:ch-example-distributive}) +into the logic formula: +\begin{equation} +\forall(A,B,C).\,\left(A\vee B\right)\wedge C=\left(A\wedge C\right)\vee\left(B\wedge C\right)\quad.\label{eq:ch-example-distributive-1} +\end{equation} +This formula is the well-known \textsf{``}distributive law\textsf{''}\footnote{See \texttt{\href{https://en.wikipedia.org/wiki/Distributive_property\#Rule_of_replacement}{https://en.wikipedia.org/wiki/Distributive\_property\#Rule\_of\_replacement}}} +valid in Boolean logic as well as in the constructive logic. Since +a logical equation $P=Q$ means $P\Rightarrow Q$ and $Q\Rightarrow P$, +the distributive law~(\ref{eq:ch-example-distributive-1}) means +that the two formulas hold: +\begin{align} + & \forall(A,B,C).\,\left(A\vee B\right)\wedge C\Rightarrow\left(A\wedge C\right)\vee\left(B\wedge C\right)\quad,\label{eq:ch-example-distributive-1a}\\ + & \forall(A,B,C).\,\left(A\wedge C\right)\vee\left(B\wedge C\right)\Rightarrow\left(A\vee B\right)\wedge C\quad.\label{eq:ch-example-distributive-1b} +\end{align} +The CH correspondence maps these logical formulas to fully parametric +functions with types: \begin{lstlisting} -(f1 andThen f2)((a,())) == f2(f1((a,())) == f2(a) == (a, ()) -(f2 andThen f1)(a) == f1(f2(a)) == f1((a, ())) = a +def f1[A, B, C]: ((Either[A, B], C)) => Either[(A, C), (B, C)] = ??? +def f2[A, B, C]: Either[(A, C), (B, C)] => (Either[A, B], C) = ??? \end{lstlisting} -Now let us write a proof in the code notation. The codes of $f_{1}$ -and $f_{2}$ are: -\[ -f_{1}\triangleq a^{:A}\times1\rightarrow a\quad,\quad\quad f_{2}\triangleq a^{:A}\rightarrow a\times1\quad, -\] -where we denoted by $1$ the value \lstinline!()! of the \lstinline!Unit! -type. We find: -\begin{align*} -(f_{1}\bef f_{2})(a^{:A}\times1) & =f_{2}\left(f_{1}(a\times1)\right)=f_{2}\left(a\right)=a\times1\quad,\\ -(f_{2}\bef f_{1})(a^{:A}) & =f_{1}(f_{2}(a))=f_{1}(a\times1)=a\quad. -\end{align*} -This shows that both compositions are identity functions. Another -way of writing the proof is by computing the function compositions -symbolically, without applying to a value $a^{:A}$: +In the type notation, these type signatures are written as: \begin{align*} -f_{1}\bef f_{2} & =\left(a\times1\rightarrow a\right)\bef\left(a\rightarrow a\times1\right)=\left(a\times1\rightarrow a\times1\right)=\text{id}^{A\times\bbnum 1}\quad,\\ -f_{2}\bef f_{1} & =\left(a\rightarrow a\times1\right)\bef\left(a\times1\rightarrow a\right)=\left(a\rightarrow a\right)=\text{id}^{A}\quad. + & f_{1}^{A,B,C}:\left(A+B\right)\times C\rightarrow A\times C+B\times C\quad,\\ + & f_{2}^{A,B,C}:A\times C+B\times C\rightarrow\left(A+B\right)\times C\quad. \end{align*} +Since the two logical formulas (\ref{eq:ch-example-distributive-1a})\textendash (\ref{eq:ch-example-distributive-1b}) +are true theorems in constructive logic, we expect to be able to implement +the functions \lstinline!f1! and \lstinline!f2!. It is not straightforward +to guess how to combine the proof rules of Table~\ref{tab:Proof-rules-of-constructive-and-boolean} +to obtain proofs of Eqs.~(\ref{eq:ch-example-distributive-1a})\textendash (\ref{eq:ch-example-distributive-1b}). +So, instead of deriving the implementations of \lstinline!f1! and +\lstinline!f2! from the CH correspondence, we will write the Scala +code directly. +To implement \lstinline!f1!, we need to perform pattern matching +on the argument: +\begin{lstlisting} +def f1[A, B, C]: ((Either[A, B], C)) => Either[(A, C), (B, C)] = { + case (Left(a), c) => Left((a, c)) // No other choice here. + case (Right(b), c) => Right((b, c)) // No other choice here. +} +\end{lstlisting} +In both cases, we have only one possible expression of the correct +type. -\subsubsection{Example \label{subsec:ch-Example-A+B}\ref{subsec:ch-Example-A+B}} - -Verify the type equivalence $A+B\cong B+A$. - -\subparagraph{Solution} - -The corresponding Scala types are \lstinline!Either[A, B]! and \lstinline!Either[B, A]!. -We use pattern matching to implement the functions required for the -type equivalence: - -\begin{wrapfigure}{l}{0.55\columnwidth}% -\vspace{-0.86\baselineskip} +Similarly, the implementation of \lstinline!f2! leaves us no choices: \begin{lstlisting} -def f1[A, B]: Either[A, B] => Either[B, A] = { - case Left(a) => Right(a) // No other choice here. - case Right(b) => Left(b) // No other choice here. +def f2[A, B, C]: Either[(A, C), (B, C)] => (Either[A, B], C) = { + case Left((a, c)) => (Left(a), c) // No other choice here. + case Right((b, c)) => (Right(b), c) // No other choice here. } -def f2[A, B]: Either[B, A] => Either[A, B] = f1[B, A] \end{lstlisting} +The code of \lstinline!f1! and \lstinline!f2! never discards any +given values; in other words, these functions appear to preserve information. +We can formulate this property rigorously as a requirement that an +arbitrary value \lstinline!x: (Either[A, B], C)! be mapped by \lstinline!f1! +to some value \lstinline!y: Either[(A, C), (B, C)]! and then mapped +by \lstinline!f2! back to \emph{the same} value \lstinline!x!. Similarly, +any value \lstinline!y! of type \lstinline!Either[(A, C), (B, C)]! +should be transformed by \lstinline!f2! and then by \lstinline!f1! +back to the same value \lstinline!y!. -\vspace{-1.2\baselineskip} -\end{wrapfigure}% -The functions \lstinline!f1! and \lstinline!f2! are implemented -by code that can be derived unambiguously from the type signatures. -For instance, the line \lstinline!case Left(a) => ...! is required -to return a value of type \lstinline!Either[B, A]! by using a given -value \lstinline!a:A!. The only way of doing that is by returning -\lstinline!Right(a)!. - -It is clear from the code that the functions \lstinline!f1! and \lstinline!f2! -are inverses of each other. To verify that rigorously, we need to -show that \lstinline!f1 andThen f2! is equal to an identity function. -The function \lstinline!f1 andThen f2! applies \lstinline!f2! to -the result of \lstinline!f1!. The code of \lstinline!f1! contains -two \lstinline!case ...! lines, each returning a result. So, we need -to apply \lstinline!f2! separately in each line. Evaluate the code -symbolically: -\begin{lstlisting} -(f1 andThen f2) == { - case Left(a) => f2(Right(a)) - case Right(b) => f2(Left(b)) -} == { - case Left(a) => Left(a) - case Right(b) => Right(b) -} -\end{lstlisting} -The result is a function of type \lstinline!Either[A, B] => Either[A, B]! -that does not change its argument; so, it is equal to the identity -function. - -Let us now write the function \lstinline!f1! in the code notation -and perform the same derivation. We will also develop a useful notation -for functions operating on disjunctive types. - -The pattern matching construction in the Scala code of \lstinline!f1! -is similar to a pair of functions with types \lstinline!A => Either[B, A]! -and \lstinline!B => Either[B, A]!. One of these functions is applied -depending on whether the argument of \lstinline!f1! has type $A+\bbnum 0$ -or $\bbnum 0+B$. So, we may write the code of \lstinline!f1! as: -\[ -f_{1}\triangleq x^{:A+B}\rightarrow\begin{cases} -\text{if }x=a^{:A}+\bbnum 0^{:B}\quad: & \bbnum 0^{:B}+a^{:A}\\ -\text{if }x=\bbnum 0^{:A}+b^{:B}\quad: & b^{:B}+\bbnum 0^{:A} -\end{cases} -\] -Since both the argument and the result of $f_{1}$ are disjunctive -types with $2$ parts each, it is convenient to write the code of -$f_{1}$ as a $2\times2$ \emph{matrix} that maps the input parts -to the output parts:\index{disjunctive type!in matrix notation}\index{matrix notation}\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.5\baselineskip} -\begin{lstlisting} -def f1[A, B]: Either[A, B] => Either[B, A] = { - case Left(a) => Right(a) - case Right(b) => Left(b) -} -\end{lstlisting} -\vspace{-2\baselineskip} -\end{wrapfigure}% -\vspace{-0.6\baselineskip} +Let us write these conditions as equations: \[ -f_{1}\triangleq\,\begin{array}{|c||cc|} - & B & A\\ -\hline A & \bbnum 0 & a^{:A}\rightarrow a\\ -B & b^{:B}\rightarrow b & \bbnum 0 -\end{array}\quad. +\forall x^{:(A+B)\times C}.\,f_{2}(f_{1}(x))=x\quad,\quad\quad\forall y^{:A\times C+B\times C}.\,f_{1}\left(f_{2}(y)\right)=y\quad. \] -\vspace{-0.5\baselineskip} - -The rows of the matrix correspond to the \lstinline!case! rows in -the Scala code. There is one row for each part of the disjunctive -type of the argument. The columns of the matrix correspond to the -parts of the disjunctive type of the result.\index{pattern matching!in matrix notation} -The matrix element in row $A$ and column $A$ is a function of type -$A\rightarrow A$ that corresponds to the line \lstinline!case Left(a) => Right(a)! -in the Scala code. The matrix element in row $A$ and column $B$ -is written as $\bbnum 0$ because no value of that type is returned. -In this way, we translate each line of the \lstinline!match / case! -expression into a code matrix. - -The code of $f_{2}$ is written similarly. Let us rename arguments -for clarity:\hfill{}~\begin{wrapfigure}[4]{l}{0.5\columnwidth}% -\vspace{-0.7\baselineskip} -\begin{lstlisting} -def f2[A, B]: Either[B, A] => Either[A, B] = { - case Left(y) => Right(y) - case Right(x) => Left(x) -} -\end{lstlisting} -\vspace{-1.5\baselineskip} -\end{wrapfigure}% +If these equations hold, it means that all the information in a value +$x^{:(A+B)\times C}$ is completely preserved inside the value $y\triangleq f_{1}(x)$; +the original value $x$ can be recovered as $x=f_{2}(y)$. Then the +function $f_{1}$ is the \textbf{inverse}\index{inverse function} +of $f_{2}$. Conversely, all the information in a value $y^{:A\times C+B\times C}$ +is preserved inside $x\triangleq f_{2}(y)$ and can be recovered by +applying $f_{1}$. Since the values $x^{:(A+B)\times C}$ and $y^{:A\times C+B\times C}$ +are arbitrary, it will follow that the \emph{data types} themselves, +$\left(A+B\right)\times C$ and $A\times C+B\times C$, carry equivalent +information. Such types are called equivalent\index{types!equivalent} +or isomorphic\index{types!isomorphic}\index{isomorphic types}. -\vspace{-1.5\baselineskip} +Generally, we say that types $P$ and $Q$ are \textbf{equivalent} +or \textbf{isomorphic} (denoted $P\cong Q$) \index{type equivalence}when +there exist functions $f_{1}:P\rightarrow Q$ and $f_{2}:Q\rightarrow P$ +that are inverses of each other. We can write these conditions using +the notation $(f_{1}\bef f_{2})(x)\triangleq f_{2}(f_{1}(x))$ as: \[ -f_{2}\triangleq\,\begin{array}{|c||cc|} - & A & B\\ -\hline B & \bbnum 0 & y^{:B}\rightarrow y\\ -A & x^{:A}\rightarrow x & \bbnum 0 -\end{array}\quad. +f_{1}\bef f_{2}=\text{id}\quad,\quad\quad f_{2}\bef f_{1}=\text{id}\quad. \] -\vspace{-0.8\baselineskip} - -\noindent The forward composition $f_{1}\bef f_{2}$ is computed by -the standard rules of row-by-column matrix multiplication.\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Matrix_multiplication}{https://en.wikipedia.org/wiki/Matrix\_multiplication}}} -Any terms containing $\bbnum 0$ are omitted, and the remaining functions -are composed: -\begin{align*} -f_{1}\bef f_{2} & =\,\begin{array}{|c||cc|} - & B & A\\ -\hline A & \bbnum 0 & a^{:A}\rightarrow a\\ -B & b^{:B}\rightarrow b & \bbnum 0 -\end{array}\,\bef\,\begin{array}{|c||cc|} - & A & B\\ -\hline B & \bbnum 0 & y^{:B}\rightarrow y\\ -A & x^{:A}\rightarrow x & \bbnum 0 -\end{array}\\ -{\color{greenunder}\text{matrix multiplication/composition}:}\quad & =\,\,\begin{array}{|c||cc|} - & A & B\\ -\hline A & (a^{:A}\rightarrow a)\bef(x^{:A}\rightarrow x) & \bbnum 0\\ -B & \bbnum 0 & (b^{:B}\rightarrow b)\bef(y^{:B}\rightarrow y) -\end{array}\\ -{\color{greenunder}\text{function composition}:}\quad & =\,\begin{array}{|c||cc|} - & A & B\\ -\hline A & \text{id} & \bbnum 0\\ -B & \bbnum 0 & \text{id} -\end{array}\,=\text{id}^{:A+B\rightarrow A+B}\quad. -\end{align*} - -Several features of the matrix notation are helpful in such calculations. -The parts of the code of $f_{1}$ are automatically composed with -the corresponding parts of the code of $f_{2}$. To check that the -types match in the function composition, we just need to compare the -types in the output row $\,\begin{array}{||cc|} -B & A\end{array}\,$ of $f_{1}$ with the input column $\,\begin{array}{|c||} -B\\ -A -\end{array}\,$ of $f_{2}$. Once we verified that all types match, we may omit the -type annotations and write the same derivation more concisely as: -\begin{align*} -f_{1}\bef f_{2} & =\,\begin{array}{||cc|} -\bbnum 0 & a^{:A}\rightarrow a\\ -b^{:B}\rightarrow b & \bbnum 0 -\end{array}\,\bef\,\begin{array}{||cc|} -\bbnum 0 & y^{:B}\rightarrow y\\ -x^{:A}\rightarrow x & \bbnum 0 -\end{array}\\ -{\color{greenunder}\text{matrix multiplication/composition}:}\quad & =\,\,\begin{array}{||cc|} -(a^{:A}\rightarrow a)\bef(x^{:A}\rightarrow x) & \bbnum 0\\ -\bbnum 0 & (b^{:B}\rightarrow b)\bef(y^{:B}\rightarrow y) -\end{array}\\ -{\color{greenunder}\text{function composition}:}\quad & =\,\begin{array}{||cc|} -\text{id} & \bbnum 0\\ -\bbnum 0 & \text{id} -\end{array}\,=\text{id}\quad. -\end{align*} -The identity function is represented by the diagonal matrix: $\,\begin{array}{||cc|} -\text{id} & \bbnum 0\\ -\bbnum 0 & \text{id} -\end{array}$~. - -\subsubsection{Exercise \label{subsec:ch-Exercise-AxB}\ref{subsec:ch-Exercise-AxB}\index{exercises}} - -Verify the type equivalence $A\times B\cong B\times A$. +(In Scala, the forward composition $f_{1}\bef f_{2}$ is the function +\lstinline!f1 andThen f2!. We omit type annotations since we already +checked that the types match.) If these conditions hold, there is +a one-to-one correspondence between values of types $P$ and $Q$. +This is the same as to say that the data types $P$ and $Q$ \textsf{``}carry +equivalent information\textsf{''}. -\subsubsection{Exercise \label{subsec:ch-Exercise-A+B+C}\ref{subsec:ch-Exercise-A+B+C}} +To verify that the Scala functions \lstinline!f1! and \lstinline!f2! +defined above are inverses of each other, we first check if $f_{1}\bef f_{2}=\text{id}$. +Applying $f_{1}\bef f_{2}$ means to apply $f_{1}$ and then to apply +$f_{2}$ to the result. Begin by applying $f_{1}$ to an arbitrary +value $x^{:(A+B)\times C}$. A value $x$ of that type can be in only +one of the two disjoint cases: a tuple \lstinline!(Left(a), c)! or +a tuple \lstinline!(Right(b), c)!, for some values \lstinline!a:A!, +\lstinline!b:B!, and \lstinline!c:C!. The Scala code of \lstinline!f1! +maps these tuples to \lstinline!Left((a, c))! and to \lstinline!Right((b, c))! +respectively; we can see this directly from the code of \lstinline!f1!. +We then apply $f_{2}$ to those values, which maps them back to a +tuple \lstinline!(Left(a), c)! or to a tuple \lstinline!(Right(b), c)! +respectively, according to the code of \lstinline!f2!. These tuples +are exactly the value $x$ we started with. So, applying $f_{1}\bef f_{2}$ +to an arbitrary $x^{:(A+B)\times C}$ returns that value $x$. This +is the same as to say that $f_{1}\bef f_{2}=\text{id}$. -Verify the type equivalence $\left(A+B\right)+C\cong A+\left(B+C\right)$. -Since Section~\ref{subsec:Logical-identity-not-type-equivalence} -proved the equivalences $\left(A+B\right)+C\cong A+\left(B+C\right)$ -and $\left(A\times B\right)\times C\cong A\times\left(B\times C\right)$, -we may write $A+B+C$ and $A\times B\times C$ without any parentheses. +To check whether $f_{2}\bef f_{1}=\text{id}$, we apply $f_{2}$ to +an arbitrary value $y^{:A\times C+B\times C}$, which must be one +of the two disjoint cases, \lstinline!Left((a, c))! or \lstinline!Right((b, c))!. +The code of \lstinline!f2! maps these two cases into tuples \lstinline!(Left(a), c)! +and \lstinline!(Right(b), c)! respectively. Then we apply \lstinline!f1! +and map these tuples back to \lstinline!Left((a, c))! and \lstinline!Right((b, c))! +respectively. It follows that applying $f_{2}$ and then $f_{1}$ +will always return the initial value. As a formula, this is written +as $f_{2}\bef f_{1}=\text{id}$. -\subsubsection{Exercise \label{subsec:ch-Exercise-A+B2}\ref{subsec:ch-Exercise-A+B2}} +By looking at the code of \lstinline!f1! and \lstinline!f2!, we +can directly observe that these functions are inverses of each other: +the tuple pattern \lstinline!(Left(a), c)! is mapped to \lstinline!Left((a, c))!, +and the pattern \lstinline!(Right(b), c)! to \lstinline!Right((b, c))!, +or vice versa. It is visually clear that no information is lost and +that the original values are returned by function compositions $f_{1}\bef f_{2}$ +or $f_{2}\bef f_{1}$. -Verify the type equivalence: -\[ -\left(A+B\right)\times\left(A+B\right)=A\times A+\bbnum 2\times A\times B+B\times B\quad, -\] -where $\bbnum 2$ denotes the \lstinline!Boolean! type\index{2@$\bbnum 2$ (the \texttt{Boolean} type)} -(which may be defined as $\bbnum 2\triangleq\bbnum 1+\bbnum 1$). +We find that the logical identity~(\ref{eq:ch-example-distributive-1}) +leads to an equivalence of the corresponding types: +\begin{equation} +\left(A+B\right)\times C\cong A\times C+B\times C\quad.\label{eq:ch-distributive-law-types} +\end{equation} +To get Eq.~(\ref{eq:ch-distributive-law-types}) from Eq.~(\ref{eq:ch-example-distributive-1}), +we need to convert a logical formula to an arithmetic expression by +replacing the disjunction operations $\vee$ by $+$ and the conjunctions +$\wedge$ by $\times$ everywhere. -\subsection{Type cardinalities and type equivalence} +As another example of a logical identity, consider the associativity +law for conjunction: +\begin{equation} +\left(\alpha\wedge\beta\right)\wedge\gamma=\alpha\wedge\left(\beta\wedge\gamma\right)\quad.\label{eq:ch-example-associativity-conjunction} +\end{equation} +The corresponding types are $(A\times B)\times C$ and $A\times(B\times C)$; +in Scala, \lstinline!((A, B), C)! and \lstinline!(A, (B, C))!. We +can define functions that convert between these types without information +loss\index{information loss}: +\begin{lstlisting} +def f3[A, B, C]: (((A, B), C)) => (A, (B, C)) = { case ((a, b), c) => + (a, (b, c)) } +def f4[A, B, C]: (A, (B, C)) => (((A, B), C)) = { case (a, (b, c)) => + ((a, b), c) } +\end{lstlisting} +By applying these functions to arbitrary values of types \lstinline!((A, B), C)! +and \lstinline!(A, (B, C))!, it is easy to see that the functions +\lstinline!f3! and \lstinline!f4! are inverses of each other. This +is also directly visible in the code: the nested tuple pattern \lstinline!((a, b), c)! +is mapped to the pattern \lstinline!(a, (b, c))! and back. So, the +types $\left(A\times B\right)\times C$ and $A\times\left(B\times C\right)$ +are equivalent, and we can write $A\times B\times C$ without parentheses. -To understand why type equivalences are related to arithmetic identities, -consider the question of how many different values a given type can -have. +Does a logical identity always correspond to an equivalence of types? +This turns out to be \emph{not} so. A simple example of a logical +identity that does not correspond to a type equivalence is: +\begin{equation} +True\vee\alpha=True\quad.\label{eq:ch-example-logic-identity-2} +\end{equation} +Since the CH correspondence maps the logical constant $True$ into +the unit type $\bbnum 1$, the type equivalence corresponding to Eq.~(\ref{eq:ch-example-logic-identity-2}) +is $\bbnum 1+A\cong\bbnum 1$. The type denoted by $\bbnum 1+A$ means +\lstinline!Option[A]! in Scala, so the corresponding equivalence +is \lstinline!Option[A]!$\cong$\lstinline!Unit!. Intuitively, this +type equivalence should not hold: an \lstinline!Option[A]! may carry +a value of type \lstinline!A!, which cannot possibly be stored in +a value of type \lstinline!Unit!. We can verify this intuition rigorously +by proving that any fully parametric functions with type signatures +$g_{1}:\bbnum 1+A\rightarrow\bbnum 1$ and $g_{2}:\bbnum 1\rightarrow\bbnum 1+A$ +will not satisfy $g_{1}\bef g_{2}=\text{id}$. To verify this, we +note that $g_{2}:\bbnum 1\rightarrow\bbnum 1+A$ must have this type +signature: +\begin{lstlisting} +def g2[A]: Unit => Option[A] = ??? +\end{lstlisting} +This function must always return \lstinline!None!, since a fully +parametric function cannot produce values of an arbitrary type \lstinline!A! +from scratch. Therefore, $g_{1}\bef g_{2}$ is also a function that +always returns \lstinline!None!. The function $g_{1}\bef g_{2}$ +has type signature $\bbnum 1+A\rightarrow\bbnum 1+A$ or, in Scala +syntax, \lstinline!Option[A] => Option[A]!, and is not equal to the +identity function, because the identity function does \emph{not} always +return \lstinline!None!. -Begin by counting the number of distinct values for simple types. -For example, the \lstinline!Unit! type has only one distinct value; -the type \lstinline!Nothing! has zero values; the \lstinline!Boolean! -type has two distinct values (\lstinline!true! and \lstinline!false!); -and the type \lstinline!Int! has $2^{32}$ distinct values. +Another example of a logical identity without a type equivalence is +the distributive law: +\begin{equation} +\forall(A,B,C).\,\left(A\wedge B\right)\vee C=\left(A\vee C\right)\wedge\left(B\vee C\right)\quad,\label{eq:ch-example-distributive-2} +\end{equation} +which is \textsf{``}dual\textsf{''} to the law~(\ref{eq:ch-example-distributive-1}), +i.e., it is obtained from Eq.~(\ref{eq:ch-example-distributive-1}) +by swapping all conjunctions ($\wedge$) with disjunctions ($\vee$). +In logic, a dual formula to an identity is also an identity. The CH +correspondence maps Eq.~(\ref{eq:ch-example-distributive-2}) into +the type equation: +\begin{equation} +\forall(A,B,C).\,\left(A\times B\right)+C=\left(A+C\right)\times\left(B+C\right)\quad.\label{eq:ch-example-incorrect-identity-2} +\end{equation} +However, the types $A\times B+C$ and $\left(A+C\right)\times\left(B+C\right)$ +are \emph{not} equivalent. To see why, look at the possible code of +a function $g_{3}:\left(A+C\right)\times\left(B+C\right)\rightarrow A\times B+C$: +\begin{lstlisting}[numbers=left] +def g3[A,B,C]: ((Either[A, C], Either[B, C])) => Either[(A, B), C] = { + case (Left(a), Left(b)) => Left((a, b)) // No other choice. + case (Left(a), Right(c)) => Right(c) // No other choice. + case (Right(c), Left(b)) => Right(c) // No other choice. + case (Right(c1), Right(c2)) => Right(c1) // Must discard c1 or c2 here! +} // May return Right(c2) instead of Right(c1) in the last line. +\end{lstlisting} +In line 5, we have a choice of returning \lstinline!Right(c1)! or +\lstinline!Right(c2)!. Whichever we choose, we will lose information\index{information loss} +because we will have discarded one of the given values \lstinline!c1!, +\lstinline!c2!. After evaluating $g_{3}$, we will not be able to +restore \emph{both} \lstinline!c1! and \lstinline!c2!, no matter +what code we write. So, the composition $g_{3}\bef g_{4}$ with any +$g_{4}$ cannot be equal to the identity function. The type equation~(\ref{eq:ch-example-incorrect-identity-2}) +is incorrect. -It is more difficult to count the number of distinct values in a type -such as \lstinline!String!, which is equivalent to a list of unknown -length, \lstinline!List[Char]!. However, each computer\textsf{'}s memory is -limited, so there will exist a maximum length for values of type \lstinline!String!. -So, the total number of possible different strings will be finite -(but will depend on the computer). +We find that a logical identity ${\cal CH}(P)={\cal CH}(Q)$ guarantees, +via the CH correspondence, that we can implement \emph{some} fully +parametric functions of types $P\rightarrow Q$ and $Q\rightarrow P$. +However, it is not guaranteed that these functions are inverses of +each other, i.e., that the type conversions $P\rightarrow Q$ or $Q\rightarrow P$ +have no information loss\index{information loss}. So, the type equivalence +$P\cong Q$ does not automatically follow from the logical identity +${\cal CH}(P)={\cal CH}(Q)$. -For a given type $A$, let us denote by $\left|A\right|$ the number -of distinct values of type $A$. The number $\left|A\right|$ is called -the \index{cardinality}\textbf{cardinality} of type $A$. This is -the same as the number of elements in the set of all values of type -$A$. Since any computer\textsf{'}s memory is finite, there will be \emph{finitely} -many different values of a given type $A$ that can exist in the computer. -So, we may assume that $\left|A\right|$ is always a finite integer -value. This assumption will simplify our reasoning. We will not actually -need to compute the precise number of, say, all the different possible -strings. It is sufficient to know that the set of all strings is finite, -so that we can denote its cardinality by $|\text{String}|$. +The CH correspondence means that for true propositions ${\cal CH}(X)$ +we can compute \emph{some} value $x$ of type $X$. However, the CH +correspondence does not guarantee that the computed value $x^{:X}$ +will satisfy any additional properties or laws. -The next step is to consider the cardinality of types such as $A\times B$ -and $A+B$. If the types $A$ and $B$ have cardinalities $\left|A\right|$ -and $\left|B\right|$, it follows that the set of all distinct pairs -\lstinline!(A, B)! has $\left|A\right|\times\left|B\right|$ elements. -So, the cardinality of the type $A\times B$ is equal to the (arithmetic) -product of the cardinalities of $A$ and $B$. The set of all pairs, -denoted in mathematics by: +\subsection{Arithmetic identity corresponds to type equivalence} + +Looking at the examples of equivalent types, we notice that correct +type equivalences correspond to \emph{arithmetical} identities rather +than \emph{logical} identities. For instance, the logical identity +in Eq.~(\ref{eq:ch-example-distributive-1}) leads to the type equivalence~(\ref{eq:ch-distributive-law-types}), +which looks like a standard identity of arithmetic, such as: \[ -\left\{ (a,b)|a\in A,b\in B\right\} \quad, +(1+10)\times20=1\times20+10\times20\quad. \] -is called the \index{Cartesian product}\textbf{Cartesian product} -of sets $A$ and $B$, and is denoted by $A\times B$. For this reason, -the tuple type is also called the \index{product type}\textbf{product -type}. Accordingly, the type notation adopts the symbol $\times$ -for the product type. +The logical identity in Eq.~(\ref{eq:ch-example-distributive-2}), +which does \emph{not} yield a type equivalence, leads to an incorrect +arithmetic equation~\ref{eq:ch-example-incorrect-identity-2}, e.g., +$\left(1\times10\right)+20\neq\left(1+20\right)\times\left(10+20\right)$. +Similarly, the associativity law~(\ref{eq:ch-example-associativity-conjunction}) +leads to a type equivalence and to the arithmetic identity: +\[ +\left(a\times b\right)\times c=a\times\left(b\times c\right)\quad, +\] +The logical identity in Eq.~(\ref{eq:ch-example-logic-identity-2}), +which does not yield a type equivalence, leads to an incorrect arithmetic +statement ($\forall a.\,1+a=1$). -The set of all distinct values of the type $A+B$, i.e., of the Scala -type \lstinline!Either[A, B]!, is a \index{labeled union}labeled -union of the set of values of the form \lstinline!Left(a)! and the -set of values of the form \lstinline!Right(b)!. It is clear that -the cardinalities of these two sets are equal to $\left|A\right|$ -and $\left|B\right|$ respectively. So, the cardinality of the type -\lstinline!Either[A, B]! is equal to $\left|A\right|+\left|B\right|$. -For this reason, disjunctive types are also called \index{sum type!see \textsf{``}disjunctive type\textsf{''}}\textbf{sum -types}, and the type notation adopts the symbol $+$ for these types. +Table~\ref{tab:Logical-identities-with-disjunction-and-conjunction} +summarizes these and other examples of logical identities and the +corresponding type equivalences. In all rows, quantifiers such as +$\forall\alpha$ or $\forall(A,B)$ are implied as necessary. -We can write our conclusions as: -\begin{align*} -\left|A\times B\right| & =\left|A\right|\times\left|B\right|\quad,\\ -\left|A+B\right| & =\left|A\right|+\left|B\right|\quad. -\end{align*} -The type notation, $A\times B$ for \lstinline!(A,B)! and $A+B$ -for \lstinline!Either[A, B]!, translates directly into type cardinalities. +Because we chose the type notation to be similar to the ordinary arithmetic +notation, it is easy to translate a possible type equivalence into +an arithmetic equation. In all cases, valid arithmetic identities +correspond to type equivalences, and failures to obtain a type equivalence +correspond to incorrect arithmetic identities. With regard to type +equivalence, types such as $A+B$ and $A\times B$ behave similarly +to arithmetic expressions such as $10+20$ and $10\times20$ and not +similarly to logical formulas such as $\alpha\vee\beta$ and $\alpha\wedge\beta$. -The last step is to notice that two types can be equivalent, $P\cong Q$, -only if their cardinalities are equal, $\left|P\right|=\left|Q\right|$. -When the cardinalities are not equal, $\left|P\right|\neq\left|Q\right|$, -it will be impossible to have a one-to-one correspondence between -the sets of values of type $P$ and values of type $Q$. So, it will -be impossible to convert values from type $P$ to type $Q$ and back -without loss of information. +\begin{table} +\begin{centering} +\begin{tabular}{|c|c|} +\hline +\textbf{\small{}Logical identity} & \textbf{\small{}Type equivalence (if it holds)}\tabularnewline +\hline +\hline +{\small{}$True\vee\alpha=True$} & {\small{}$\bbnum 1+A\not\cong\bbnum 1$}\tabularnewline +\hline +{\small{}$True\wedge\alpha=\alpha$} & {\small{}$\bbnum 1\times A\cong A$}\tabularnewline +\hline +{\small{}$False\vee\alpha=\alpha$} & {\small{}$\bbnum 0+A\cong A$}\tabularnewline +\hline +{\small{}$False\wedge\alpha=False$} & {\small{}$\bbnum 0\times A\cong\bbnum 0$}\tabularnewline +\hline +{\small{}$\alpha\vee\beta=\beta\vee\alpha$} & {\small{}$A+B\cong B+A$}\tabularnewline +\hline +{\small{}$\alpha\wedge\beta=\beta\wedge\alpha$} & {\small{}$A\times B\cong B\times A$}\tabularnewline +\hline +{\small{}$\left(\alpha\vee\beta\right)\vee\gamma=\alpha\vee\left(\beta\vee\gamma\right)$} & {\small{}$\left(A+B\right)+C\cong A+\left(B+C\right)$}\tabularnewline +\hline +{\small{}$\left(\alpha\wedge\beta\right)\wedge\gamma=\alpha\wedge\left(\beta\wedge\gamma\right)$} & {\small{}$\left(A\times B\right)\times C\cong A\times\left(B\times C\right)$}\tabularnewline +\hline +{\small{}$\left(\alpha\vee\beta\right)\wedge\gamma=\left(\alpha\wedge\gamma\right)\vee\left(\beta\wedge\gamma\right)$} & {\small{}$\left(A+B\right)\times C\cong A\times C+B\times C$}\tabularnewline +\hline +{\small{}$\left(\alpha\wedge\beta\right)\vee\gamma=\left(\alpha\vee\gamma\right)\wedge\left(\beta\vee\gamma\right)$} & {\small{}$\left(A\times B\right)+C\not\cong\left(A+C\right)\times\left(B+C\right)$}\tabularnewline +\hline +\end{tabular} +\par\end{centering} +\caption{Logic identities with disjunction and conjunction, and the possible +type equivalences.\label{tab:Logical-identities-with-disjunction-and-conjunction}} +\end{table} -We conclude that types are equivalent when a logical identity \emph{and} -an arithmetic identity hold. +We already verified the first line and the last three lines of Table~\ref{tab:Logical-identities-with-disjunction-and-conjunction}. +Other identities are verified in a similar way. Let us begin with +lines 3 and 4 of Table~\ref{tab:Logical-identities-with-disjunction-and-conjunction}, +which involve the proposition $False$ and the corresponding \index{void type}void +type $\bbnum 0$ (Scala\textsf{'}s \lstinline!Nothing!). Reasoning about the +void type needs a special technique that we will now develop while +verifying the type isomorphisms $\bbnum 0\times A\cong\bbnum 0$ and +$\bbnum 0+A\cong A$. -The presence of both identities does not automatically guarantee a -useful type equivalence. The fact that information in one type can -be identically stored in another type does not necessarily mean that -it is helpful to do so in a given application. +\subsubsection{Example \label{subsec:ch-Example-0-times-A}\ref{subsec:ch-Example-0-times-A}\index{examples (with code)}} -For example, the types \lstinline!Option[Option[A]]! and \lstinline!Either[Boolean, A]! -are equivalent because both types contain $2+\left|A\right|$ distinct -values. The short notation for these types is $\bbnum 1+\bbnum 1+A$ -and $\bbnum 2+A$ respectively. The type Boolean is denoted by $\bbnum 2$ -since it has only \emph{two} distinct values. +Verify the type equivalence $\bbnum 0\times A\cong\bbnum 0$. -One could write code for converting between these types without loss -of information: -\begin{lstlisting} -def f1[A]: Option[Option[A]] => Either[Boolean, A] = { - case None => Left(false) // Or maybe Left(true)? - case Some(None) => Left(true) - case Some(Some(x)) => Right(x) -} +\subparagraph{Solution} -def f2[A]: Either[Boolean, A] => Option[Option[A]] = { - case Left(false) => None - case Left(true) => Some(None) - case Right(x) => Some(Some(x)) -} -\end{lstlisting} -The presence of an arbitrary choice in this code is a warning sign. -In \lstinline!f1!, we could map \lstinline!None! to \lstinline!Left(false)! -or to \lstinline!Left(true)! and adjust the rest of the code accordingly. -The type equivalence holds with either choice. So, these types \emph{are} -equivalent, but there is no \textsf{``}natural\textsf{''} choice of the conversion -functions \lstinline!f1! and \lstinline!f2! that will work correctly -in all applications, because the meaning of these data types will -be application-dependent. We call this type equivalence \textbf{accidental}\index{type equivalence!accidental}. +Recall that the type notation $\bbnum 0\times A$ represents the Scala +tuple type \lstinline!(Nothing, A)!. To demonstrate that the type +\lstinline!(Nothing, A)! is equivalent to the type \lstinline!Nothing!, +we need to show that the type \lstinline!(Nothing, A)! has \emph{no} +values. Indeed, how could we create a value of type, say, \lstinline!(Nothing, Int)!? +We would need to fill \emph{both} parts of the tuple. We have values +of type \lstinline!Int!, but we can never get a value of type \lstinline!Nothing!. +So, regardless of the type \lstinline!A!, it is impossible to create +any values of type \lstinline!(Nothing, A)!. In other words, the +set of values of the type \lstinline!(Nothing, A)! is empty. But +that is the definition of the void type \lstinline!Nothing!. The +types \lstinline!(Nothing, A)! (denoted by $\bbnum 0\times A$) and +\lstinline!Nothing! (denoted by $\bbnum 0$) are both void and therefore +equivalent. -\subsubsection{Example \label{subsec:ch-Example-cardinality-option-either}\ref{subsec:ch-Example-cardinality-option-either}\index{solved examples}} +\subsubsection{Example \label{subsec:ch-Example-0-plus-A}\ref{subsec:ch-Example-0-plus-A}} -Are the types \lstinline!Option[A]! and \lstinline!Either[Unit, A]! -equivalent? Check whether the corresponding logic identity and arithmetic -identity hold. +Verify the type equivalence $\bbnum 0+A\cong A$. -\paragraph{Solution} +\subparagraph{Solution} -Begin by writing the given types in the type notation: \lstinline!Option[A]! -is written as $\bbnum 1+A$, and \lstinline!Either[Unit, A]! is written -also as $\bbnum 1+A$. The notation already indicates that the types -are equivalent. But let us verify explicitly that the type notation -is not misleading us here. +The type notation $\bbnum 0+A$ corresponds to the Scala type \lstinline!Either[Nothing, A]!. +We need to show that any value of that type can be mapped without +loss of information to a value of type \lstinline!A!, and vice versa. +This means implementing functions $f_{1}:\bbnum 0+A\rightarrow A$ +and $f_{2}:A\rightarrow\bbnum 0+A$ such that $f_{1}\bef f_{2}=\text{id}$ +and $f_{2}\bef f_{1}=\text{id}$. -To establish the type equivalence, we need to implement two fully -parametric functions: +The argument of $f_{1}$ is of type \lstinline!Either[Nothing, A]!. +How can we create a value of that type? Our only choices are to create +a \lstinline!Left(x)! with \lstinline!x:Nothing!, or to create a +\lstinline!Right(y)! with \lstinline!y:A!. However, we cannot create +a value \lstinline!x! of type \lstinline!Nothing! because the type +\lstinline!Nothing! has \emph{no} values. We cannot create a \lstinline!Left(x)!. +The only remaining possibility is to create a \lstinline!Right(y)! +with some value \lstinline!y! of type \lstinline!A!. So, any values +of type $\bbnum 0+A$ must be of the form \lstinline!Right(y)!, and +we can extract that \lstinline!y! to obtain a value of type \lstinline!A!: \begin{lstlisting} -def f1[A]: Option[A] => Either[Unit, A] = ??? -def f2[A]: Either[Unit, A] => Option[A] = ??? +def f1[A]: Either[Nothing, A] => A = { + case Right(y) => y + // No need for `case Left(x) => ...` since no `x` can ever be given as `Left(x)`. +} \end{lstlisting} -These functions must satisfy $f_{1}\bef f_{2}=\text{id}$ and $f_{2}\bef f_{1}=\text{id}$. -It is straightforward to implement \lstinline!f1! and \lstinline!f2!: -\begin{lstlisting} -def f1[A]: Option[A] => Either[Unit, A] = { - case None => Left(()) - case Some(x) => Right(x) -} -def f2[A]: Either[Unit, A] => Option[A] = { - case Left(()) => None - case Right(x) => Some(x) -} +For the same reason, there is only one implementation of the function +\lstinline!f2!: +\begin{lstlisting} +def f2[A]: A => Either[Nothing, A] = { y => Right(y) } \end{lstlisting} -The code clearly shows that \lstinline!f1! and \lstinline!f2! are -inverses of each other. This verifies the type equivalence. - -The logic identity is $True\vee A=True\vee A$ and holds trivially. -It remains to check the arithmetic identity, which relates the cardinalities -of types \lstinline!Option[A]! and \lstinline!Either[Unit, A]!. -Assume that the cardinality of type \lstinline!A! is $\left|A\right|$. -Any possible value of type \lstinline!Option[A]! must be either \lstinline!None! -or \lstinline!Some(x)!, where \lstinline!x! is a value of type \lstinline!A!. -So, the number of distinct values of type \lstinline!Option[A]! is -$1+\left|A\right|$. All possible values of type \lstinline!Either[Unit, A]! -are of the form \lstinline!Left(())! or \lstinline!Right(x)!, where -\lstinline!x! is a value of type \lstinline!A!. So, the cardinality -of type \lstinline!Either[Unit, A]! is $1+\left|A\right|$. We see -that the arithmetic identity holds: the types \lstinline!Option[A]! -and \lstinline!Either[Unit, A]! have equally many distinct values. - -This example shows that the type notation is helpful for reasoning -about type equivalences. The answer was found immediately when we -wrote the type notation ($\bbnum 1+A$) for the given types. - -\subsection{Type equivalence involving function types} - -Until now, we have looked at product types and disjunctive types. -We now turn to type constructions involving function types. +It is clear from the code that the functions \lstinline!f1! and \lstinline!f2! +are inverses of each other. -Consider two types $A$ and $B$ having known cardinalities $\left|A\right|$ -and $\left|B\right|$. How many distinct values does the function -type $A\rightarrow B$ have? A function \lstinline!f: A => B! needs -to select a value of type $B$ for each possible value of type $A$. -Therefore, the number of different functions \lstinline!f: A => B! -is $\left|B\right|^{\left|A\right|}$. +We have just seen that a value of type $\bbnum 0+A$ is always a \lstinline!Right(y)! +with some \lstinline!y:A!. Similarly, a value of type $A+\bbnum 0$ +is always a \lstinline!Left(x)! with some \lstinline!x:A!. So, we +will use the notation $A+\bbnum 0$ and $\bbnum 0+A$ to \emph{denote} +the \lstinline!Left! and the \lstinline!Right! parts of the disjunctive +type \lstinline!Either!. This notation agrees with the behavior of +the Scala compiler, which will infer the types \lstinline!Either[A, Nothing] !or +\lstinline!Either[Nothing, A]! for these parts: +\begin{lstlisting} +def toLeft[A, B]: A => Either[A, B] = x => Left(x) +def toRight[A, B]: B => Either[A, B] = y => Right(y) -Here $\left|B\right|^{\left|A\right|}$ denotes the \textbf{numeric -exponent}\index{exponent}, that is, $\left|B\right|$ to the power -$\left|A\right|$. We use the numeric exponent notation ($a^{b}$) -only when computing with numbers. When denoting types and code, this -book uses superscripts for type parameters and type annotations. +scala> toLeft(123) +res0: Either[Int, Nothing] = Left(123) -For the types $A=B=\text{Int}$, we have $\left|A\right|=\left|B\right|=2^{32}$, -and the exponential formula gives: +scala> toRight("abc") +res1: Either[Nothing, String] = Right("abc") +\end{lstlisting} +We can write the functions \lstinline!toLeft! and \lstinline!toRight! +in a code notation as: \[ -\left|A\rightarrow B\right|=(2^{32})^{\left(2^{32}\right)}=2^{32\times2^{32}}=2^{2^{37}}\approx10^{4.1\times10^{10}}\quad. +\text{toLeft}^{A,B}\triangleq x^{:A}\rightarrow x+\bbnum 0^{:B}\quad,\quad\quad\text{toRight}^{A,B}\triangleq y^{:B}\rightarrow\bbnum 0^{:A}+y\quad. \] -This number greatly exceeds the number of atoms in the observable -Universe!\footnote{Estimated to be between $10^{78}$ and $10^{82}$, see \texttt{\href{https://www.universetoday.com/36302/atoms-in-the-universe/amp/}{https://www.universetoday.com/36302/atoms-in-the-universe/amp/}}} -However, almost all of those functions will map integers to integers -in extremely complicated (and practically useless) ways. The code -of those functions will be much larger than the available memory of -a realistic computer. So, the number of practically implementable -functions of type $A\rightarrow B$ can be much smaller than $\left|B\right|^{\left|A\right|}$. -Since the code of a function is a string of bytes that needs to fit -into the computer\textsf{'}s memory, the number of implementable functions -is no larger than the number of possible byte strings. - -Nevertheless, the formula $\left|B\right|^{\left|A\right|}$ is useful -since it shows the number of distinct functions that are possible -in principle. When types $A$ and $B$ have only a small number of -distinct values (for example, $A=$ \lstinline!Option[Boolean]]! -and $B=$ \lstinline!Either[Boolean, Boolean]!), the formula $\left|B\right|^{\left|A\right|}$ -gives an exact and practically relevant answer. - -Let us now look for logic identities and arithmetic identities involving -function types. Table~\ref{tab:Logical-identities-with-function-types} -lists the available identities and the corresponding type equivalences. -(In the last column, we defined $a\triangleq\left|A\right|$, $b\triangleq\left|B\right|$, -and $c\triangleq\left|C\right|$ for brevity.) - -It is notable that no logic identity is available for the formula -$\alpha\Rightarrow\left(\beta\vee\gamma\right)$, and correspondingly -no type equivalence is available for the type expression $A\rightarrow B+C$ -(although there is an identity for $A\rightarrow B\times C$). Reasoning -about types of the form $A\rightarrow B+C$ is more complicated because -those types usually cannot be transformed into simpler types. +In this notation, a value of the disjunctive type is shown without +using Scala class names such as \lstinline!Either!, \lstinline!Right!, +and \lstinline!Left!. This shortens the writing and speeds up code +reasoning. -\begin{table} -\begin{centering} -\begin{tabular}{|c|c|c|} -\hline -\textbf{\small{}Logical identity (if holds)} & \textbf{\small{}Type equivalence} & \textbf{\small{}Arithmetic identity}\tabularnewline -\hline -\hline -{\small{}$\left(True\Rightarrow\alpha\right)=\alpha$} & {\small{}$\bbnum 1\rightarrow A\cong A$} & {\small{}$a^{1}=a$}\tabularnewline -\hline -{\small{}$\left(False\Rightarrow\alpha\right)=True$} & {\small{}$\bbnum 0\rightarrow A\cong\bbnum 1$} & {\small{}$a^{0}=1$}\tabularnewline -\hline -{\small{}$\left(\alpha\Rightarrow True\right)=True$} & {\small{}$A\rightarrow\bbnum 1\cong\bbnum 1$} & {\small{}$1^{a}=1$}\tabularnewline -\hline -{\small{}$\left(\alpha\Rightarrow False\right)\neq False$} & {\small{}$A\rightarrow\bbnum 0\not\cong\bbnum 0$} & {\small{}$0^{a}\neq0$}\tabularnewline -\hline -{\small{}$\left(\alpha\vee\beta\right)\Rightarrow\gamma=\left(\alpha\Rightarrow\gamma\right)\wedge\left(\beta\Rightarrow\gamma\right)$} & {\small{}$A+B\rightarrow C\cong\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)$} & {\small{}$c^{a+b}=c^{a}\times c^{b}$}\tabularnewline -\hline -{\small{}$(\alpha\wedge\beta)\Rightarrow\gamma=\alpha\Rightarrow\left(\beta\Rightarrow\gamma\right)$} & {\small{}$A\times B\rightarrow C\cong A\rightarrow B\rightarrow C$} & {\small{}$c^{a\times b}=(c^{b})^{a}$}\tabularnewline -\hline -{\small{}$\alpha\Rightarrow\left(\beta\wedge\gamma\right)=\left(\alpha\Rightarrow\beta\right)\wedge\left(\alpha\Rightarrow\gamma\right)$} & {\small{}$A\rightarrow B\times C\cong\left(A\rightarrow B\right)\times\left(A\rightarrow C\right)$} & {\small{}$\left(b\times c\right)^{a}=b^{a}\times c^{a}$}\tabularnewline -\hline -\end{tabular} -\par\end{centering} -\caption{Logical identities with implication, and the corresponding type equivalences -and arithmetic identities.\label{tab:Logical-identities-with-function-types}} -\end{table} +The type annotation $\bbnum 0^{:A}$ is helpful to remind ourselves +about the type parameter $A$ used, e.g., by the disjunctive value +$\bbnum 0^{:A}+y^{:B}$ in the body of \lstinline!toRight[A, B]!. +Without this type annotation, $\bbnum 0+y^{:B}$ means a value of +type \lstinline!Either[A, B]! where the parameter $A$ should be +determined by matching the types of other expressions. When it is +clear what types are being used, we may omit type annotations and +write simply $\bbnum 0+y$ instead of $\bbnum 0^{:A}+y^{:B}$. -We will now prove some of the type identities in Table~\ref{tab:Logical-identities-with-function-types}. +In the notation $\bbnum 0+y^{:B}$, we use the symbol $\bbnum 0$ +rather than an ordinary zero ($0$), to avoid suggesting that $0$ +is a value of type $\bbnum 0$. The void type $\bbnum 0$ has \emph{no} +values, unlike the \lstinline!Unit! type, $\bbnum 1$, which has +a value denoted by $1$ in the code notation. -\subsubsection{Example \label{subsec:ch-Example-type-identity-f}\ref{subsec:ch-Example-type-identity-f}\index{solved examples}} +\subsubsection{Example \label{subsec:ch-Example-1xA}\ref{subsec:ch-Example-1xA}} -Verify the type equivalence $\bbnum 1\rightarrow A\cong A$. +Verify the type equivalence $A\times\bbnum 1\cong A$. \subparagraph{Solution} -Recall that the type notation $\bbnum 1\rightarrow A$ means the Scala -function type \lstinline!Unit => A!. There is only one value of type -\lstinline!Unit!. The choice of a function of type \lstinline!Unit => A! -is the same as the choice of a value of type \lstinline!A!. So, the -type $\bbnum 1\rightarrow A$ has $\left|A\right|$ distinct values, -and the arithmetic identity holds. - -To verify the type equivalence explicitly, we need to implement two -functions: -\begin{lstlisting} -def f1[A]: (Unit => A) => A = ??? -def f2[A]: A => Unit => A = ??? -\end{lstlisting} -The first function needs to produce a value of type \lstinline!A!, -given an argument of the function type \lstinline!Unit => A!. The -only possibility is to apply that function to the value of type \lstinline!Unit!. -We can always produce that value as \lstinline!()!: -\begin{lstlisting} -def f1[A]: (Unit => A) => A = (h: Unit => A) => h(()) -\end{lstlisting} -Implementing \lstinline!f2! is straightforward. We can just discard -the \lstinline!Unit! argument: -\begin{lstlisting} -def f2[A]: A => Unit => A = (x: A) => _ => x -\end{lstlisting} -It remains to show that the functions \lstinline!f1! and \lstinline!f2! -are inverses of each other. Let us show the proof using Scala code -and then using the code notation. - -Writing Scala code, compute \lstinline!f1(f2(x))! for an arbitrary -\lstinline!x:A!. Using the code of \lstinline!f1! and \lstinline!f2!, -we get: -\begin{lstlisting} -f1(f2(x)) == f1(_ => x) == (_ => x)(()) == x -\end{lstlisting} -Now compute \lstinline!f2(f1(h))! for arbitrary \lstinline!h: Unit => A! -in Scala code: +The corresponding Scala types are the tuple \lstinline!(A, Unit)! +and the type \lstinline!A!. We need to implement functions $f_{1}:\forall A.\,A\times\bbnum 1\rightarrow A$ +and $f_{2}:\forall A.\,A\rightarrow A\times\bbnum 1$ and to demonstrate +that they are inverses of each other. The Scala code for these functions +is: \begin{lstlisting} -f2(f1(h)) == f2(h(())) == { _ => h(()) } +def f1[A]: ((A, Unit)) => A = { case (a, ()) => a } +def f2[A]: A => (A, Unit) = { a => (a, ()) } \end{lstlisting} -How can we show that the function \lstinline!{_ => h(())}! is equal -to \lstinline!h!? Whenever we apply equal functions to equal arguments, -they return equal results. In our case, the argument of \lstinline!h! -is of type \lstinline!Unit!, so we only need to verify that the result -of applying \lstinline!h! to the value \lstinline!()! is the same -as the result of applying \lstinline!{_ => h(())}! to \lstinline!()!. -In other words, we need to apply both sides to an additional argument -\lstinline!()!: +Let us first write a proof by reasoning directly with Scala code: \begin{lstlisting} -f2(f1(h))(()) == { _ => h(()) } (()) == h(()) +(f1 andThen f2)((a,())) == f2(f1((a,())) == f2(a) == (a, ()) +(f2 andThen f1)(a) == f1(f2(a)) == f1((a, ())) = a \end{lstlisting} -This completes the proof. - -For comparison, let us show the same proof in the code notation. The -functions $f_{1}$ and $f_{2}$ are: +Now let us write a proof in the code notation. The codes of $f_{1}$ +and $f_{2}$ are: \[ -f_{1}\triangleq h^{:\bbnum 1\rightarrow A}\rightarrow h(1)\quad,\quad\quad f_{2}\triangleq x^{:A}\rightarrow1\rightarrow x\quad. +f_{1}\triangleq a^{:A}\times1\rightarrow a\quad,\quad\quad f_{2}\triangleq a^{:A}\rightarrow a\times1\quad, \] -Now write the function compositions in both directions: +where we denoted by $1$ the value \lstinline!()! of the \lstinline!Unit! +type. We find: \begin{align*} -{\color{greenunder}\text{expect to equal }\text{id}:}\quad & f_{1}\bef f_{2}=(h^{:\bbnum 1\rightarrow A}\rightarrow h(1))\bef(x^{:A}\rightarrow1\rightarrow x)\\ -{\color{greenunder}\text{compute composition}:}\quad & \quad=h^{:\bbnum 1\rightarrow A}\rightarrow\gunderline{1\rightarrow h(1)}\\ -{\color{greenunder}\text{note that }1\rightarrow h(1)\text{ is the same as }h:}\quad & \quad=(h^{:\bbnum 1\rightarrow A}\rightarrow h)=\text{id}\quad. +(f_{1}\bef f_{2})(a^{:A}\times1) & =f_{2}\left(f_{1}(a\times1)\right)=f_{2}\left(a\right)=a\times1\quad,\\ +(f_{2}\bef f_{1})(a^{:A}) & =f_{1}(f_{2}(a))=f_{1}(a\times1)=a\quad. \end{align*} +This shows that both compositions are identity functions. Another +way of writing the proof is by computing the function compositions +symbolically, without applying to a value $a^{:A}$: \begin{align*} -{\color{greenunder}\text{expect to equal }\text{id}:}\quad & f_{2}\bef f_{1}=(x^{:A}\rightarrow1\rightarrow x)\bef(h^{:\bbnum 1\rightarrow A}\rightarrow h(1))\\ -{\color{greenunder}\text{compute composition}:}\quad & \quad=x^{:A}\rightarrow\gunderline{(1\rightarrow x)(1)}\\ -{\color{greenunder}\text{apply function}:}\quad & \quad=(x^{:A}\rightarrow x)=\text{id}\quad. +f_{1}\bef f_{2} & =\left(a\times1\rightarrow a\right)\bef\left(a\rightarrow a\times1\right)=\left(a\times1\rightarrow a\times1\right)=\text{id}^{A\times\bbnum 1}\quad,\\ +f_{2}\bef f_{1} & =\left(a\rightarrow a\times1\right)\bef\left(a\times1\rightarrow a\right)=\left(a\rightarrow a\right)=\text{id}^{A}\quad. \end{align*} -The type $\bbnum 1\rightarrow A$ is equivalent to the type $A$ in -the sense of carrying the same information, but these types are not -exactly the same. An important difference between these types is that -a value of type $A$ is available immediately, while a value of type -$\bbnum 1\rightarrow A$ is a function that still needs to be applied -to an argument (of type $\bbnum 1$) before a value of type $A$ is -obtained. The type $\bbnum 1\rightarrow A$ may represent an \textsf{``}on-call\textsf{''}\index{on-call value} -value of type $A$. That value is computed on demand every time it -is requested. (See Section~\ref{subsec:Lazy-values-iterators-and-streams} -for more details about \textsf{``}on-call\textsf{''} values.) - -The void type\index{void type} $\bbnum 0$ needs special reasoning, -as the next examples show: -\subsubsection{Example \label{subsec:ch-Example-type-identity-0-to-A}\ref{subsec:ch-Example-type-identity-0-to-A}} +\subsubsection{Example \label{subsec:ch-Example-A+B}\ref{subsec:ch-Example-A+B}} -Verify the type equivalence $\bbnum 0\rightarrow A\cong\bbnum 1$. +Verify the type equivalence $A+B\cong B+A$. \subparagraph{Solution} -To verify that a type $X$ is equivalent to the \lstinline!Unit! -type, we need to show that there is only one distinct value of type -$X$. So, let us find out how many values the type $\bbnum 0\rightarrow A$ -has. Consider a value of that type, which is a function $f^{:\bbnum 0\rightarrow A}$ -from the type $\bbnum 0$ to a type $A$. Since there exist no values -of type $\bbnum 0$, the function $f$ will never be applied to any -arguments and so \emph{does not need} to compute any actual values -of type $A$. So, $f$ is a function whose body may be \textsf{``}empty\textsf{''}. -At least, $f$\textsf{'}s body does not need to contain any expressions of -type $A$. In Scala, such a function can be written as: -\begin{lstlisting} -def absurd[A]: Nothing => A = { ??? } -\end{lstlisting} -This code will compile without type errors. An equivalent code is: +The corresponding Scala types are \lstinline!Either[A, B]! and \lstinline!Either[B, A]!. +Use pattern matching to implement the functions required for the type +equivalence: \begin{lstlisting} -def absurd[A]: Nothing => A = { x => ??? } +def f1[A, B]: Either[A, B] => Either[B, A] = { + case Left(a) => Right(a) // No other choice here. + case Right(b) => Left(b) // No other choice here. +} +def f2[A, B]: Either[B, A] => Either[A, B] = f1[B, A] \end{lstlisting} -The symbol \lstinline!???! is defined in the Scala library and represents -code that is \textsf{``}not implemented\textsf{''}. Trying to evaluate this symbol -will produce an error: +The functions \lstinline!f1! and \lstinline!f2! are implemented +by code that can be derived unambiguously from the type signatures. +For instance, the line \lstinline!case Left(a) => ...! is required +to return a value of type \lstinline!Either[B, A]! by using a given +value \lstinline!a:A!. The only way of doing that is by returning +\lstinline!Right(a)!. + +It is clear from the code that the functions \lstinline!f1! and \lstinline!f2! +are inverses of each other. To verify that rigorously, we need to +show that \lstinline!f1 andThen f2! is equal to an identity function. +The function \lstinline!f1 andThen f2! applies \lstinline!f2! to +the result of \lstinline!f1!. The code of \lstinline!f1! contains +two \lstinline!case ...! lines, each returning a result. So, we need +to apply \lstinline!f2! separately in each line. Evaluate the code +symbolically: \begin{lstlisting} -scala> val x = ??? -scala.NotImplementedError: an implementation is missing +(f1 andThen f2) == { + case Left(a) => f2(Right(a)) + case Right(b) => f2(Left(b)) +} == { + case Left(a) => Left(a) + case Right(b) => Right(b) +} \end{lstlisting} -Since the function \lstinline!absurd! can never be applied to an -argument, this error will never happen. So, one can pretend that the -result value (which will never be computed) has any required type, -e.g., the type $A$. In this way, the compiler will accept the definition -of \lstinline!absurd!. - -Let us now verify that there exists \emph{only one} distinct function -of type $\bbnum 0\rightarrow A$. Take any two functions of that type, -$f^{:\bbnum 0\rightarrow A}$ and $g^{:\bbnum 0\rightarrow A}$. Are -they different? The only way of showing that $f$ and $g$ are different -is by finding a value $x$ such that $f(x)\neq g(x)$. But then $x$ -would be of type $\bbnum 0$, and there are \emph{no} \emph{values} -of type $\bbnum 0$. So, we will never be able to find the required -value $x$. It follows that any two functions $f$ and $g$ of type -$\bbnum 0\rightarrow A$ are equal, $f=g$. In other words, there -exists only one distinct value of type $\bbnum 0\rightarrow A$. Since -the cardinality of the type $\bbnum 0\rightarrow A$ is $1$, we obtain -the type equivalence $\bbnum 0\rightarrow A\cong\bbnum 1$. - -\subsubsection{Example \label{subsec:ch-Example-type-identity-A-0}\ref{subsec:ch-Example-type-identity-A-0}} - -Show that $A\rightarrow\bbnum 0\not\cong\bbnum 0$ and $A\rightarrow\bbnum 0\not\cong\bbnum 1$, -where $A$ is an arbitrary type. - -\subparagraph{Solution} - -To prove that two types are \emph{not} equivalent, it is sufficient -to show that their cardinalities are different. Let us determine the -cardinality of the type $A\rightarrow\bbnum 0$, assuming that the -cardinality of $A$ is known. We note that a function of type, say, -$\text{Int}\rightarrow\bbnum 0$ is impossible to implement. (If we -had such a function $f^{:\text{Int}\rightarrow\bbnum 0}$, we could -evaluate, say, $x\triangleq f(123)$ and obtain a value $x$ of type -$\bbnum 0$, which is impossible by definition of the type $\bbnum 0$. -It follows that $\left|\text{Int}\rightarrow\bbnum 0\right|=0$. However, -Example~\ref{subsec:ch-Example-type-identity-0-to-A} shows that -$\bbnum 0\rightarrow\bbnum 0$ has cardinality $1$. So, we find that -$\left|A\rightarrow\bbnum 0\right|=1$ if the type $A$ is itself -$\bbnum 0$ but $\left|A\rightarrow\bbnum 0\right|=0$ for all other -types $A$. We conclude that the type $A\rightarrow\bbnum 0$ is not -equivalent to $\bbnum 0$ or $\bbnum 1$ when $A$ is an unknown type. -The type $A\rightarrow\bbnum 0$ is void for non-void types $A$, -and vice versa. - -\subsubsection{Example \label{subsec:ch-Example-type-identity-2}\ref{subsec:ch-Example-type-identity-2}} - -Verify the type equivalence $A\rightarrow\bbnum 1\cong\bbnum 1$. +The result is a function of type \lstinline!Either[A, B] => Either[A, B]! +that does not change its argument; so, it is equal to the identity +function. -\subparagraph{Solution} +Let us now write the function \lstinline!f1! in the code notation +and perform the same derivation. We will also develop a useful notation +for functions operating on disjunctive types. -There is only one fully parametric function that returns $\bbnum 1$: +The pattern matching construction in the Scala code of \lstinline!f1! +is similar to a pair of functions with types \lstinline!A => Either[B, A]! +and \lstinline!B => Either[B, A]!. One of these functions is applied +depending on whether the argument of \lstinline!f1! has type $A+\bbnum 0$ +or $\bbnum 0+B$. So, we may write the code of \lstinline!f1! as: +\[ +f_{1}\triangleq x^{:A+B}\rightarrow\begin{cases} +\text{if }x=a^{:A}+\bbnum 0^{:B}\quad: & \bbnum 0^{:B}+a^{:A}\\ +\text{if }x=\bbnum 0^{:A}+b^{:B}\quad: & b^{:B}+\bbnum 0^{:A} +\end{cases} +\] +Since both the argument and the result of $f_{1}$ are disjunctive +types with $2$ parts each, it is convenient to write the code of +$f_{1}$ as a $2\times2$ \emph{matrix} that maps the input parts +to the output parts:\index{disjunctive type!in matrix notation}\index{matrix notation} \begin{lstlisting} -def f[A]: A => Unit = { _ => () } +def f1[A, B]: Either[A, B] => Either[B, A] = { + case Left(a) => Right(a) + case Right(b) => Left(b) +} \end{lstlisting} -The function $f$ cannot use its argument of type $A$ since nothing -is known about the type $A$. So, the code of $f$ \emph{must} discard -its argument and return the fixed value \lstinline!()! of type \lstinline!Unit!. -In the code notation, this function is written as: \[ -f^{:A\rightarrow\bbnum 1}\triangleq\_\rightarrow1\quad. +f_{1}\triangleq\,\begin{array}{|c||cc|} + & B & A\\ +\hline A & \bbnum 0 & a^{:A}\rightarrow a\\ +B & b^{:B}\rightarrow b & \bbnum 0 +\end{array}\quad. \] -We can show that there exists only \emph{one} distinct function of -type $A\rightarrow\bbnum 1$ (that is, the type $A\rightarrow\bbnum 1$ -has cardinality $1$). Assume that $f$ and $g$ are two such functions, -and try to find a value $x^{:A}$ such that $f(x)\neq g(x)$. We cannot -find any such $x$ because $f(x)=1$ and $g(x)=1$ for all $x$. So, -any two functions $f$ and $g$ of type $A\rightarrow\bbnum 1$ must -be equal to each other. The cardinality of the type $A\rightarrow\bbnum 1$ -is $1$. - -Any type having cardinality $1$ is equivalent to the \lstinline!Unit! -type ($\bbnum 1$). So, $A\rightarrow\bbnum 1\cong\bbnum 1$. - -\subsubsection{Example \label{subsec:ch-Example-type-identity-6-1}\ref{subsec:ch-Example-type-identity-6-1}} - -Denote by $\_^{:A}\rightarrow B$ the type of \index{constant function}constant -functions of type $A\rightarrow B$ (functions that ignore their argument). -Show that the type $\_^{:A}\rightarrow B$ is equivalent to the type -$B$, as long as $A\neq\bbnum 0$. - -\subparagraph{Solution} +The rows of the matrix correspond to the \lstinline!case! rows in +the Scala code. There is one row for each part of the disjunctive +type of the argument. The columns of the matrix correspond to the +parts of the disjunctive type of the result.\index{pattern matching!in matrix notation} +The matrix element in row $A$ and column $A$ is a function of type +$A\rightarrow A$ that corresponds to the line \lstinline!case Left(a) => Right(a)! +in the Scala code. The matrix element in row $A$ and column $B$ +is written as $\bbnum 0$ because no value of that type is returned. +In this way, we translate each line of the \lstinline!match / case! +expression into a code matrix. -An isomorphism between the types $B$ and $\_^{:A}\rightarrow B$ -is given by the two functions: +The code of $f_{2}$ is written similarly. Let us rename arguments +for clarity:\hfill{} +\begin{lstlisting} +def f2[A, B]: Either[B, A] => Either[A, B] = { + case Left(y) => Right(y) + case Right(x) => Left(x) +} +\end{lstlisting} +\[ +f_{2}\triangleq\,\begin{array}{|c||cc|} + & A & B\\ +\hline B & \bbnum 0 & y^{:B}\rightarrow y\\ +A & x^{:A}\rightarrow x & \bbnum 0 +\end{array}\quad. +\] +The forward composition $f_{1}\bef f_{2}$ is computed by the standard +rules of row-by-column matrix multiplication.\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Matrix_multiplication}{https://en.wikipedia.org/wiki/Matrix\_multiplication}}} +Any terms containing $\bbnum 0$ are omitted, and the remaining functions +are composed: \begin{align*} - & f_{1}:B\rightarrow\_^{:A}\rightarrow B\quad,\quad\quad f_{1}\triangleq b\rightarrow\_\rightarrow b\quad;\\ - & f_{2}:(\_^{:A}\rightarrow B)\rightarrow B\quad,\quad\quad f_{2}\triangleq k^{:\_\rightarrow B}\rightarrow k(x^{:A})\quad, +f_{1}\bef f_{2} & =\,\begin{array}{|c||cc|} + & B & A\\ +\hline A & \bbnum 0 & a^{:A}\rightarrow a\\ +B & b^{:B}\rightarrow b & \bbnum 0 +\end{array}\,\bef\,\begin{array}{|c||cc|} + & A & B\\ +\hline B & \bbnum 0 & y^{:B}\rightarrow y\\ +A & x^{:A}\rightarrow x & \bbnum 0 +\end{array}\\ +{\color{greenunder}\text{matrix composition}:}\quad & =\,\,\begin{array}{|c||cc|} + & A & B\\ +\hline A & \gunderline{(a^{:A}\rightarrow a)\bef(x^{:A}\rightarrow x)} & \bbnum 0\\ +B & \bbnum 0 & \gunderline{(b^{:B}\rightarrow b)\bef(y^{:B}\rightarrow y)} +\end{array}\\ +{\color{greenunder}\text{function composition}:}\quad & =\,\begin{array}{|c||cc|} + & A & B\\ +\hline A & \text{id} & \bbnum 0\\ +B & \bbnum 0 & \text{id} +\end{array}\,=\text{id}^{:A+B\rightarrow A+B}\quad. \end{align*} -where $x$ is any value of type $A$. That value exists since the -type $A$ is not void. The function $f_{2}$ does not depend on the -choice of $x$ because $k$ is a constant function, so $k(x)$ is -the same for all $x$. In other words, the function $k$ satisfies -$k=(\_\rightarrow k(x))$ with any chosen $x$. To prove that $f_{1}$ -and $f_{2}$ are inverses: + +Several features of the matrix notation are helpful in such calculations. +The parts of the code of $f_{1}$ are automatically composed with +the corresponding parts of the code of $f_{2}$. To check that the +types match in the function composition, we just need to compare the +types in the output row $\,\begin{array}{||cc|} +B & A\end{array}\,$ of $f_{1}$ with the input column $\,\begin{array}{|c||} +B\\ +A +\end{array}\,$ of $f_{2}$. Once we verified that all types match, we may omit the +type annotations and write the same derivation more concisely as: \begin{align*} - & f_{1}\bef f_{2}=(b\rightarrow\_\rightarrow b)\bef(k\rightarrow k(x))=b\rightarrow(\_\rightarrow b)(x)=(b\rightarrow b)=\text{id}\quad,\\ - & f_{2}\bef f_{1}=(k\rightarrow k(x))\bef(b\rightarrow\_\rightarrow b)=k\rightarrow\_\rightarrow k(x)=k\rightarrow k=\text{id}\quad. +f_{1}\bef f_{2} & =\,\begin{array}{||cc|} +\bbnum 0 & a^{:A}\rightarrow a\\ +b^{:B}\rightarrow b & \bbnum 0 +\end{array}\,\bef\,\begin{array}{||cc|} +\bbnum 0 & y^{:B}\rightarrow y\\ +x^{:A}\rightarrow x & \bbnum 0 +\end{array}\\ +{\color{greenunder}\text{matrix composition}:}\quad & =\,\,\begin{array}{||cc|} +\gunderline{(a^{:A}\rightarrow a)\bef(x^{:A}\rightarrow x)} & \bbnum 0\\ +\bbnum 0 & \gunderline{(b^{:B}\rightarrow b)\bef(y^{:B}\rightarrow y)} +\end{array}\\ +{\color{greenunder}\text{function composition}:}\quad & =\,\begin{array}{||cc|} +\text{id} & \bbnum 0\\ +\bbnum 0 & \text{id} +\end{array}\,=\text{id}\quad. \end{align*} +The identity function is represented by the diagonal matrix: $\,\begin{array}{||cc|} +\text{id} & \bbnum 0\\ +\bbnum 0 & \text{id} +\end{array}$~. +\subsubsection{Exercise \label{subsec:ch-Exercise-AxB}\ref{subsec:ch-Exercise-AxB}\index{exercises}} -\subsubsection{Example \label{subsec:ch-Example-type-identity-5}\ref{subsec:ch-Example-type-identity-5}} +Verify the type equivalence $A\times B\cong B\times A$. -Verify the following type equivalence: +\subsubsection{Exercise \label{subsec:ch-Exercise-A+B+C}\ref{subsec:ch-Exercise-A+B+C}} + +Verify the type equivalence $\left(A+B\right)+C\cong A+\left(B+C\right)$. +Since Section~\ref{subsec:Logical-identity-not-type-equivalence} +proved the equivalences $\left(A+B\right)+C\cong A+\left(B+C\right)$ +and $\left(A\times B\right)\times C\cong A\times\left(B\times C\right)$, +we may write $A+B+C$ and $A\times B\times C$ without any parentheses. + +\subsubsection{Exercise \label{subsec:ch-Exercise-A+B2}\ref{subsec:ch-Exercise-A+B2}} + +Verify the type equivalence: \[ -A+B\rightarrow C\cong(A\rightarrow C)\times(B\rightarrow C)\quad. +\left(A+B\right)\times\left(A+B\right)=A\times A+\bbnum 2\times A\times B+B\times B\quad, \] +where $\bbnum 2$ denotes the \lstinline!Boolean! type\index{2@$\bbnum 2$ (the \texttt{Boolean} type)} +(which may be defined as $\bbnum 2\triangleq\bbnum 1+\bbnum 1$). +\subsection{Type cardinalities and type equivalence} -\subparagraph{Solution} +To understand why type equivalences are related to arithmetic identities, +consider the question of how many different values a given type can +have. -Begin by implementing two functions with type signatures: -\begin{lstlisting} -def f1[A,B,C]: (Either[A, B] => C) => (A => C, B => C) = ??? -def f2[A,B,C]: ((A => C, B => C)) => Either[A, B] => C = ??? -\end{lstlisting} -The code can be derived unambiguously from the type signatures. For -the first function, we need to produce a pair of functions of type -\lstinline!(A => C, B => C)!. Can we produce the first part of that -pair? Computing a function of type \lstinline!A => C! means that -we need to produce a value of type \lstinline!C! given an arbitrary -value \lstinline!a:A!. The available data is a function of type \lstinline!Either[A, B] => C! -called, say, \lstinline!h!. We can apply that function to \lstinline!Left(a)! -and obtain a value of type \lstinline!C! as required. So, a function -of type \lstinline!A => C! is computed as \lstinline!a => h(Left(a))!. -We can produce a function of type \lstinline!B => C! similarly. The -code is: -\begin{lstlisting} -def f1[A,B,C]: (Either[A, B] => C) => (A => C, B => C) = - (h: Either[A, B] => C) => (a => h(Left(a)), b => h(Right(b))) -\end{lstlisting} -We write this function in the code notation like this: -\begin{align*} - & f_{1}:\left(A+B\rightarrow C\right)\rightarrow\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)\quad,\\ - & f_{1}\triangleq h^{:A+B\rightarrow C}\rightarrow\big(a^{:A}\rightarrow h(a+\bbnum 0^{:B})\big)\times\big(b^{:B}\rightarrow h(\bbnum 0^{:A}+b)\big)\quad. -\end{align*} +Begin by counting the number of distinct values for simple types. +For example, the \lstinline!Unit! type has only one distinct value; +the type \lstinline!Nothing! has zero values; the \lstinline!Boolean! +type has two distinct values (\lstinline!true! and \lstinline!false!); +and the type \lstinline!Int! has $2^{32}$ distinct values. -For the function \lstinline!f2!, we need to apply pattern matching -to both curried arguments and then return a value of type \lstinline!C!. -This can be achieved in only one way: -\begin{lstlisting} -def f2[A,B,C](f: A => C, g: B => C): Either[A, B] => C = { - case Left(a) => f(a) - case Right(b) => g(b) -} -\end{lstlisting} -We write this function in the code notation like this: -\begin{align*} - & f_{2}:\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)\rightarrow A+B\rightarrow C\quad,\\ - & f_{2}\triangleq f^{:A\rightarrow C}\times g^{:B\rightarrow C}\rightarrow\,\begin{array}{|c||c|} - & C\\ -\hline A & a\rightarrow f(a)\\ -B & b\rightarrow g(b) -\end{array}\quad. -\end{align*} -The matrix in the last line has only one column because the result -type, $C$, is not known to be a disjunctive type. We may also simplify -the functions, e.g., replace $a\rightarrow f(a)$ by just $f$, and -write: -\[ -f_{2}\triangleq f^{:A\rightarrow C}\times g^{:B\rightarrow C}\rightarrow\,\begin{array}{|c||c|} - & C\\ -\hline A & f\\ -B & g -\end{array}\quad. -\] +It is more difficult to count the number of distinct values in a type +such as \lstinline!String!, which is equivalent to a list of unknown +length, \lstinline!List[Char]!. However, each computer\textsf{'}s memory is +limited, so there will exist a maximum length for values of type \lstinline!String!. +So, the total number of possible different strings will be finite +(but will depend on the computer). -It remains to verify that $f_{1}\bef f_{2}=\text{id}$ and $f_{2}\bef f_{1}=\text{id}$. -To compute $f_{1}\bef f_{2}$, we write (omitting types): -\begin{align*} -f_{1}\bef f_{2} & =\big(h\rightarrow(a\rightarrow h(a+\bbnum 0))\times(b\rightarrow h(\bbnum 0+b))\big)\bef\bigg(f\times g\rightarrow\,\begin{array}{||c|} -f\\ -g -\end{array}\,\bigg)\\ -{\color{greenunder}\text{compute composition}:}\quad & =h\rightarrow\,\begin{array}{||c|} -a\rightarrow h(a+\bbnum 0)\\ -b\rightarrow h(\bbnum 0+b) -\end{array}\quad. -\end{align*} -To proceed, we need to simplify the expressions $h(a+\bbnum 0)$ and -$h(\bbnum 0+b)$. We rewrite the argument $h$ (an arbitrary function -of type $A+B\rightarrow C$) in the matrix notation: -\[ -h\triangleq\,\begin{array}{|c||c|} - & C\\ -\hline A & a\rightarrow p(a)\\ -B & b\rightarrow q(b) -\end{array}\,=\,\begin{array}{|c||c|} - & C\\ -\hline A & p\\ -B & q -\end{array}\quad, -\] -where $p^{:A\rightarrow C}$ and $q^{:B\rightarrow C}$ are new arbitrary -functions. Since we already checked the types, we can omit all type -annotations and write $h$ as: -\[ -h\triangleq\,\begin{array}{||c|} -p\\ -q -\end{array}\quad. -\] -To evaluate expressions such as $h(a+\bbnum 0)$ and $h(\bbnum 0+b)$, -we need to use one of the rows of this matrix. The correct row will -be selected \emph{automatically} by the rules of matrix multiplication -if we place a row vector to the left of the matrix and use the convention -of omitting terms containing $\bbnum 0$: +For a given type $A$, let us denote by $\left|A\right|$ the number +of distinct values of type $A$. The number $\left|A\right|$ is called +the \index{cardinality}\textbf{cardinality} of type $A$. This is +the same as the number of elements in the set of all values of type +$A$. Since any computer\textsf{'}s memory is finite, there will be \emph{finitely} +many different values of a given type $A$ that can exist in the computer. +So, we may assume that $\left|A\right|$ is always a finite integer +value. This assumption will simplify our reasoning. We will not actually +need to compute the precise number of, say, all the different possible +strings. It is sufficient to know that the set of all strings is finite, +so that we can denote its cardinality by $|\text{String}|$. + +The next step is to consider the cardinality of types such as $A\times B$ +and $A+B$. If the types $A$ and $B$ have cardinalities $\left|A\right|$ +and $\left|B\right|$, it follows that the set of all distinct pairs +\lstinline!(A, B)! has $\left|A\right|\times\left|B\right|$ elements. +So, the cardinality of the type $A\times B$ is equal to the (arithmetic) +product of the cardinalities of $A$ and $B$. The set of all pairs, +denoted in mathematics by: \[ -\begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array}\,=a\triangleright p\quad,\quad\quad\begin{array}{|cc|} -\bbnum 0 & b\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array}\,=b\triangleright q\quad. +\left\{ (a,b)|a\in A,b\in B\right\} \quad, \] -Here we used the symbol $\triangleright$ to separate an argument -from a function when the argument is written to the \emph{left} of -the function. The symbol $\triangleright$ (pronounced \textsf{``}pipe\textsf{''}\index{pipe notation}\index{triangleright-notation@$\triangleright$-notation!see \textsf{``}pipe notation\textsf{''}}) -is defined by $x\triangleright f\triangleq f(x)$. In Scala, this -operation is available as \lstinline!x.pipe(f)! as of Scala 2.13. - -We can write values of disjunctive types, such as $a+\bbnum 0$, as -row vectors $\,\begin{array}{|cc|} -a & \bbnum 0\end{array}\,$: -\begin{equation} -h(a+\bbnum 0)=(a+\bbnum 0)\triangleright h=\,\begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright\,h\quad.\label{eq:forward-notation-} -\end{equation} -With these notations, we compute further. Omit all terms applying -$\bbnum 0$ or applying something to $\bbnum 0$: -\begin{align*} - & \begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright\,h=\,\begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array}\,=a\triangleright p=p(a)\quad,\\ - & \begin{array}{|cc|} -\bbnum 0 & b\end{array}\,\triangleright\,h=\,\,\begin{array}{|cc|} -\bbnum 0 & b\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array}\,=b\triangleright q=q(b)\quad. -\end{align*} -Now we can complete the proof of $f_{1}\bef f_{2}=\text{id}$: -\begin{align*} -f_{1}\bef f_{2} & =h\rightarrow\,\begin{array}{||c|} -a\rightarrow h(a+\bbnum 0)\\ -b\rightarrow h(\bbnum 0+b) -\end{array}\\ -{\color{greenunder}\text{previous equations}:}\quad & =\,\begin{array}{||c|} -p\\ -q -\end{array}\,\rightarrow\,\begin{array}{||c|} -a\rightarrow p(a)\\ -b\rightarrow q(b) -\end{array}\\ -{\color{greenunder}\text{simplify functions}:}\quad & =\,\,\begin{array}{||c|} -p\\ -q -\end{array}\,\rightarrow\,\begin{array}{||c|} -p\\ -q -\end{array}\,=\text{id}\quad. -\end{align*} +is called the \index{Cartesian product}\textbf{Cartesian product} +of sets $A$ and $B$, and is denoted by $A\times B$. For this reason, +the tuple type is also called the \index{product type}\textbf{product +type}. Accordingly, the type notation adopts the symbol $\times$ +for the product type. -To prove that $f_{2}\bef f_{1}=\text{id}$, use the notation~(\ref{eq:forward-notation-}): -\begin{align*} - & f_{2}\bef f_{1}=\bigg(f\times g\rightarrow\,\begin{array}{||c|} -f\\ -g -\end{array}\,\bigg)\bef\big(h\rightarrow(a\rightarrow(a+\bbnum 0)\triangleright h)\times(b\rightarrow(\bbnum 0+b)\triangleright h)\big)\\ -{\color{greenunder}\text{compute composition}:}\quad & =f\times g\rightarrow\big(a\rightarrow\gunderline{\,\begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright}\,\begin{array}{||c|} -f\\ -g -\end{array}\,\big)\times\big(b\rightarrow\gunderline{\,\begin{array}{|cc|} -\bbnum 0 & b\end{array}\,\triangleright}\,\begin{array}{||c|} -f\\ -g -\end{array}\,\big)\\ -{\color{greenunder}\text{apply functions}:}\quad & =f\times g\rightarrow(a\rightarrow\gunderline{a\triangleright f})\times(b\rightarrow\gunderline{b\triangleright g})\\ -{\color{greenunder}\text{definition of }\triangleright:}\quad & =f\times g\rightarrow\gunderline{\left(a\rightarrow f(a)\right)}\times\gunderline{\left(b\rightarrow g(b)\right)}\\ -{\color{greenunder}\text{simplify functions}:}\quad & =\left(f\times g\rightarrow f\times g\right)=\text{id}\quad. -\end{align*} +The set of all distinct values of the type $A+B$, i.e., of the Scala +type \lstinline!Either[A, B]!, is a \index{labeled union}labeled +union of the set of values of the form \lstinline!Left(a)! and the +set of values of the form \lstinline!Right(b)!. It is clear that +the cardinalities of these two sets are equal to $\left|A\right|$ +and $\left|B\right|$ respectively. So, the cardinality of the type +\lstinline!Either[A, B]! is equal to $\left|A\right|+\left|B\right|$. +For this reason, disjunctive types are also called \index{sum type!see \textsf{``}disjunctive type\textsf{''}}\textbf{sum +types}, and the type notation adopts the symbol $+$ for these types. -In this way, we have proved that $f_{1}$ and $f_{2}$ are mutual -inverses. The proofs appear long because we took time to motivate -and introduce new notation for applying matrices to row vectors. Once -this notation is understood, the proof for $f_{1}\bef f_{2}=\text{id}$ -can be written as: +We can write our conclusions as: \begin{align*} -f_{1}\bef f_{2} & =\left(h\rightarrow(a\rightarrow(a+\bbnum 0)\triangleright h)\times(b\rightarrow(\bbnum 0+b)\triangleright h)\right)\bef\bigg(f\times g\rightarrow\,\begin{array}{||c|} -f\\ -g -\end{array}\bigg)\\ -{\color{greenunder}\text{compute composition}:}\quad & =h\rightarrow\,\begin{array}{||c|} -\,a\,\rightarrow\,\left|\begin{array}{cc} -a & \bbnum 0\end{array}\right|\triangleright h\\ -b\rightarrow\left|\begin{array}{cc} -\bbnum 0 & b\end{array}\right|\triangleright h -\end{array}\,=\,\begin{array}{||c|} -p\\ -q -\end{array}\rightarrow\,\begin{array}{||c|} -a\rightarrow\,\begin{array}{|cc|} -a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array}\\ -b\,\rightarrow\,\begin{array}{|cc|} -\bbnum 0 & b\,\,\end{array}\,\triangleright\,\begin{array}{||c|} -p\\ -q -\end{array} -\end{array}\\ -{\color{greenunder}\text{apply functions}:}\quad & =\,\begin{array}{||c|} -p\\ -q -\end{array}\,\rightarrow\,\begin{array}{||c|} -a\rightarrow a\triangleright p\\ -b\rightarrow b\triangleright q -\end{array}\,=\,\begin{array}{||c|} -p\\ -q -\end{array}\,\rightarrow\,\begin{array}{||c|} -p\\ -q -\end{array}\,=\text{id}\quad. +\left|A\times B\right| & =\left|A\right|\times\left|B\right|\quad,\\ +\left|A+B\right| & =\left|A\right|+\left|B\right|\quad. \end{align*} -Proofs in the code notation are shorter than in Scala syntax because -certain names and keywords (such as \lstinline!Left!, \lstinline!Right!, -\lstinline!case!, \lstinline!match!, etc.) are omitted. From now -on, we will prefer to use the code notation in proofs, keeping in -mind that one can always convert the code notation to Scala. +The type notation, $A\times B$ for \lstinline!(A,B)! and $A+B$ +for \lstinline!Either[A, B]!, translates directly into type cardinalities. -Note that the function arrow ($\rightarrow$) binds weaker than the -pipe operation ($\triangleright$), so the code notation $x\rightarrow y\triangleright z$ -means $x\rightarrow(y\triangleright z)$. We will review the code -notation more systematically in Chapter~\ref{chap:Reasoning-about-code}. +The last step is to notice that two types can be equivalent, $P\cong Q$, +only if their cardinalities are equal, $\left|P\right|=\left|Q\right|$. +When the cardinalities are not equal, $\left|P\right|\neq\left|Q\right|$, +it will be impossible to have a one-to-one correspondence between +the sets of values of type $P$ and values of type $Q$. So, it will +be impossible to convert values from type $P$ to type $Q$ and back +without loss of information. -\subsubsection{Example \label{subsec:ch-Example-type-identity-6}\ref{subsec:ch-Example-type-identity-6}} - -Verify the type equivalence: -\[ -A\times B\rightarrow C\cong A\rightarrow B\rightarrow C\quad. -\] +We conclude that types are equivalent when a logical identity \emph{and} +an arithmetic identity hold. +The presence of both identities does not automatically guarantee a +useful type equivalence. The fact that information in one type can +be identically stored in another type does not necessarily mean that +it is helpful to do so in a given application. -\subparagraph{Solution} +For example, the types \lstinline!Option[Option[A]]! and \lstinline!Either[Boolean, A]! +are equivalent because both types contain $2+\left|A\right|$ distinct +values. The short notation for these types is $\bbnum 1+\bbnum 1+A$ +and $\bbnum 2+A$ respectively. The type Boolean is denoted by $\bbnum 2$ +since it has only \emph{two} distinct values. -Begin by implementing the two functions: -\begin{lstlisting} -def f1[A,B,C]: (((A, B)) => C) => A => B => C = ??? -def f2[A,B,C]: (A => B => C) => ((A, B)) => C = ??? -\end{lstlisting} -The Scala code can be derived from the type signatures unambiguously: +One could write code for converting between these types without loss +of information: \begin{lstlisting} -def f1[A,B,C]: (((A, B)) => C) => A => B => C = g => a => b => g((a, b)) -def f2[A,B,C]: (A => B => C) => ((A, B)) => C = h => { case (a, b) => h(a)(b) } +def f1[A]: Option[Option[A]] => Either[Boolean, A] = { + case None => Left(false) // Or maybe Left(true)? + case Some(None) => Left(true) + case Some(Some(x)) => Right(x) +} + +def f2[A]: Either[Boolean, A] => Option[Option[A]] = { + case Left(false) => None + case Left(true) => Some(None) + case Right(x) => Some(Some(x)) +} \end{lstlisting} -Write these functions in the code notation: -\begin{align*} - & f_{1}=g^{:A\times B\rightarrow C}\rightarrow a^{:A}\rightarrow b^{:B}\rightarrow g(a\times b)\quad,\\ - & f_{2}=h^{:A\rightarrow B\rightarrow C}\rightarrow\left(a\times b\right)^{:A\times B}\rightarrow h(a)(b)\quad. -\end{align*} -We denote by $\left(a\times b\right)^{:A\times B}$ the argument of -type \lstinline!(A, B)! with pattern matching implied. This notation -allows us to write shorter code formulas involving tupled arguments. +The presence of an arbitrary choice in this code is a warning sign. +In \lstinline!f1!, we could map \lstinline!None! to \lstinline!Left(false)! +or to \lstinline!Left(true)! and adjust the rest of the code accordingly. +The type equivalence holds with either choice. So, these types \emph{are} +equivalent, but there is no \textsf{``}natural\textsf{''} choice of the conversion +functions \lstinline!f1! and \lstinline!f2! that will work correctly +in all applications, because the meaning of these data types will +be application-dependent. We call this type equivalence \textbf{accidental}\index{type equivalence!accidental}. -Compute the function composition $f_{1}\bef f_{2}$ and show that -it is equal to an identity function: -\begin{align*} -{\color{greenunder}\text{expect to equal }\text{id}:}\quad & f_{1}\bef f_{2}=(g\rightarrow\gunderline{a\rightarrow b\rightarrow g(a\times b)})\bef\left(h\rightarrow a\times b\rightarrow h(a)(b)\right)\\ -{\color{greenunder}\text{substitute }h=a\rightarrow b\rightarrow g(a\times b):}\quad & \quad=g\rightarrow\gunderline{a\times b\rightarrow g(a\times b)}\\ -{\color{greenunder}\text{simplify function}:}\quad & \quad=\left(g\rightarrow g\right)=\text{id}\quad. -\end{align*} -Compute the function composition $f_{2}\bef f_{1}$ and show that -it is equal to an identity function: -\begin{align*} -{\color{greenunder}\text{expect to equal }\text{id}:}\quad & f_{2}\bef f_{1}=(h\rightarrow\gunderline{a\times b\rightarrow h(a)(b)})\bef\left(g\rightarrow a\rightarrow b\rightarrow g(a\times b)\right)\\ -{\color{greenunder}\text{substitute }g=a\times b\rightarrow h(a)(b):}\quad & \quad=h\rightarrow a\rightarrow\gunderline{b\rightarrow h(a)(b)}\\ -{\color{greenunder}\text{simplify function }b\rightarrow h(a)(b):}\quad & \quad=h\rightarrow\gunderline{a\rightarrow h(a)}\\ -{\color{greenunder}\text{simplify function }a\rightarrow h(a)\text{ to }h:}\quad & \quad=\left(h\rightarrow h\right)=\text{id}\quad. -\end{align*} +\subsubsection{Example \label{subsec:ch-Example-cardinality-option-either}\ref{subsec:ch-Example-cardinality-option-either}\index{examples (with code)}} +Are the types \lstinline!Option[A]! and \lstinline!Either[Unit, A]! +equivalent? Check whether the corresponding logic identity and arithmetic +identity hold. -\section{Summary} +\paragraph{Solution} -What tasks can we perform now? -\begin{itemize} -\item Convert a fully parametric type signature into a logical formula and: -\begin{itemize} -\item Decide whether the type signature can be implemented in code. -\item If possible, derive the code using the CH correspondence. -\end{itemize} -\item Use the type notation (Table~\ref{tab:ch-correspondence-type-notation-CH-propositions}) -for reasoning about types to: -\begin{itemize} -\item Decide type equivalence using the rules in Tables~\ref{tab:Logical-identities-with-disjunction-and-conjunction}\textendash \ref{tab:Logical-identities-with-function-types}. -\item Simplify type expressions before writing code. -\end{itemize} -\item Use the matrix notation and the pipe notation to write code that works -on disjunctive types. -\end{itemize} -What tasks \emph{cannot} be performed with these tools? -\begin{itemize} -\item Automatically generate code for \emph{recursive} functions. The CH -correspondence is based on propositional logic, which cannot describe -recursion. Accordingly, recursion is absent from the eight code constructions -of Section~\ref{subsec:The-rules-of-proof}. Recursive functions -need to be coded by hand. -\item Automatically generate code satisfying a property (e.g., isomorphism). -We may generate some code, but the CH correspondence does not guarantee -that properties will hold. We need to verify the required properties -manually, after deriving the code. -\item Express complicated conditions (e.g., \textsf{``}array is sorted\textsf{''}) in a -type signature. This can be done using \textbf{dependent types}\index{dependent type} -(i.e., types that directly depend on values in some way). This is -an advanced technique beyond the scope of this book. Programming languages -such as Coq, Agda, and Idris fully support dependent types, while -Scala has only limited support. -\item Generate code using type constructors with known methods (e.g., the -\lstinline!map! method). -\end{itemize} -As an example of using type constructors with known methods, consider -this type signature: +Begin by writing the given types in the type notation: \lstinline!Option[A]! +is written as $\bbnum 1+A$, and \lstinline!Either[Unit, A]! is written +also as $\bbnum 1+A$. The notation already indicates that the types +are equivalent. But let us verify explicitly that the type notation +is not misleading us here. + +To establish the type equivalence, we need to implement two fully +parametric functions: \begin{lstlisting} -def q[A]: Array[A] => (A => Option[B]) => Array[Option[B]] +def f1[A]: Option[A] => Either[Unit, A] = ??? +def f2[A]: Either[Unit, A] => Option[A] = ??? \end{lstlisting} -Can we generate the code of this function from its type signature? -We know that the Scala library defines a \lstinline!map! method on -the \lstinline!Array! class. So, an implementation of \lstinline!q! -is: +These functions must satisfy $f_{1}\bef f_{2}=\text{id}$ and $f_{2}\bef f_{1}=\text{id}$. +It is straightforward to implement \lstinline!f1! and \lstinline!f2!: \begin{lstlisting} -def q[A]: Array[A] => (A => Option[B]) => Array[Option[B]] = { arr => f => arr.map(f) } +def f1[A]: Option[A] => Either[Unit, A] = { + case None => Left(()) + case Some(x) => Right(x) +} +def f2[A]: Either[Unit, A] => Option[A] = { + case Left(()) => None + case Right(x) => Some(x) +} \end{lstlisting} -However, it is hard to create an \emph{algorithm} that can derive -this implementation automatically from the type signature of \lstinline!q! -via the Curry-Howard correspondence. The algorithm would have to convert -the type signature of \lstinline!q! into this logical formula: -\begin{equation} -{\cal CH}(\text{Array}^{A})\Rightarrow{\cal CH}(A\rightarrow\text{Opt}^{B})\Rightarrow{\cal CH}(\text{Array}^{\text{Opt}^{B}})\quad.\label{eq:ch-example-quantified-proposition} -\end{equation} -To derive an implementation, the algorithm would need to use the available -\lstinline!map! method for \lstinline!Array!. That method has the -type signature: -\[ -\text{map}:\forall(A,B).\,\text{Array}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Array}^{B}\quad. -\] -To derive the ${\cal CH}$-proposition~(\ref{eq:ch-example-quantified-proposition}), -the algorithm will need to assume that the ${\cal CH}$-proposition: -\begin{equation} -{\cal CH}\,\big(\forall(A,B).\,\text{Array}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Array}^{B}\big)\label{eq:ch-example-quantified-proposition-2} -\end{equation} -already holds. In other words Eq.~(\ref{eq:ch-example-quantified-proposition-2}) -is one of the premises of a sequent. Reasoning about premises such -as Eq.~(\ref{eq:ch-example-quantified-proposition-2}) requires \index{first-order logic}\textbf{first-order -logic} \textemdash{} a logic whose proof rules can handle quantified -types such as $\forall(A,B)$\emph{ }inside premises. However, first-order -logic is \textbf{undecidable}\index{undecidable logic}: no algorithm -can find a proof (or verify the absence of a proof) in all cases. +The code clearly shows that \lstinline!f1! and \lstinline!f2! are +inverses of each other. This verifies the type equivalence. -The constructive propositional logic with the rules listed in Table~\ref{tab:Proof-rules-for-constructive-logic} -is \textbf{decidable},\index{decidable logic} i.e., it has an algorithm -that either finds a proof or disproves any given formula. However, -that logic cannot handle type constructors such as $\text{Array}^{A}$. -It also cannot handle premises containing type quantifiers such as -$\forall(A,B)$, because all the available rules have the quantifiers -placed \emph{outside} the premises. +The logic identity is $True\vee A=True\vee A$ and holds trivially. +It remains to check the arithmetic identity, which relates the cardinalities +of types \lstinline!Option[A]! and \lstinline!Either[Unit, A]!. +Assume that the cardinality of type \lstinline!A! is $\left|A\right|$. +Any possible value of type \lstinline!Option[A]! must be either \lstinline!None! +or \lstinline!Some(x)!, where \lstinline!x! is a value of type \lstinline!A!. +So, the number of distinct values of type \lstinline!Option[A]! is +$1+\left|A\right|$. All possible values of type \lstinline!Either[Unit, A]! +are of the form \lstinline!Left(())! or \lstinline!Right(x)!, where +\lstinline!x! is a value of type \lstinline!A!. So, the cardinality +of type \lstinline!Either[Unit, A]! is $1+\left|A\right|$. We see +that the arithmetic identity holds: the types \lstinline!Option[A]! +and \lstinline!Either[Unit, A]! have equally many distinct values. -So, code for functions such as \lstinline!q! can only be derived -by trial and error, informed by intuition. This book will help programmers -to acquire the necessary intuition and technique. +This example shows that the type notation is helpful for reasoning +about type equivalences. The answer was found immediately when we +wrote the type notation ($\bbnum 1+A$) for the given types. -\subsection{Solved examples\index{solved examples}} +\subsection{Type equivalence involving function types} -\subsubsection{Example \label{subsec:ch-solvedExample-1}\ref{subsec:ch-solvedExample-1}} +Until now, we have looked at product types and disjunctive types. +We now turn to type constructions involving function types. -Find the cardinality of the type \lstinline!P = Option[Option[Boolean] => Boolean]!. -Write \lstinline!P! in the type notation and simplify it to an equivalent -type. +Consider two types $A$ and $B$ having known cardinalities $\left|A\right|$ +and $\left|B\right|$. How many distinct values does the function +type $A\rightarrow B$ have? A function \lstinline!f: A => B! needs +to select a value of type $B$ for each possible value of type $A$. +Therefore, the number of different functions \lstinline!f: A => B! +is $\left|B\right|^{\left|A\right|}$. -\subparagraph{Solution} +Here $\left|B\right|^{\left|A\right|}$ denotes the \textbf{numeric +exponent}\index{exponent}, that is, $\left|B\right|$ to the power +$\left|A\right|$. We use the numeric exponent notation ($a^{b}$) +only when computing with numbers. When denoting types and code, this +book uses superscripts for type parameters and type annotations. -Begin with the type \lstinline!Option[Boolean]!, which can be either -\lstinline!None! or \lstinline!Some(x)! with an \lstinline!x:Boolean!. -Since the type \lstinline!Boolean! has $2$ possible values, the -type \lstinline!Option[Boolean]! has $3$ values: -\[ -|\text{Opt}^{\text{Boolean}}|=\left|\bbnum 1+\text{Boolean}\right|=1+\left|\text{Boolean}\right|=1+2=3\quad. -\] -In the type notation, \lstinline!Boolean! is denoted by the symbol -$\bbnum 2$, and the type \lstinline!Option[Boolean]! by $\bbnum 1+\bbnum 2$. -So, the type notation $\bbnum 1+\bbnum 2$ is consistent with the -cardinality $3$ of that type: +For the types $A=B=\text{Int}$, we have $\left|A\right|=\left|B\right|=2^{32}$, +and the exponential formula gives: \[ -\left|\bbnum 1+\text{Boolean}\right|=\left|\bbnum 1+\bbnum 2\right|=1+2=3\quad. +\left|A\rightarrow B\right|=(2^{32})^{\left(2^{32}\right)}=2^{32\times2^{32}}=2^{2^{37}}\approx10^{4.1\times10^{10}}\quad. \] +This number greatly exceeds the number of atoms in the observable +Universe.\footnote{Estimated in \texttt{\href{https://www.universetoday.com/36302/atoms-in-the-universe/amp/}{https://www.universetoday.com/36302/atoms-in-the-universe/amp/}} +to be between $10^{78}$ and $10^{82}$.} However, almost all of those functions will map integers to integers +in extremely complicated (and practically useless) ways. The code +of those functions will be much larger than the available memory of +a realistic computer. So, the number of practically implementable +functions of type $A\rightarrow B$ can be much smaller than $\left|B\right|^{\left|A\right|}$. +Since the code of a function is a string of bytes that needs to fit +into the computer\textsf{'}s memory, the number of implementable functions +is no larger than the number of possible byte strings. -The function type \lstinline!Option[Boolean] => Boolean! is denoted -by $\bbnum 1+\bbnum 2\rightarrow\bbnum 2$. Compute its cardinality -as: -\[ -|\text{Opt}^{\text{Boolean}}\rightarrow\text{Boolean}|=\left|\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right|=\left|\bbnum 2\right|^{\left|\bbnum 1+\bbnum 2\right|}=2^{3}=8\quad. -\] -Finally, the we write \lstinline!P! in the type notation as $P=\bbnum 1+\left(\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right)$ -and find: -\[ -\left|P\right|=\left|\bbnum 1+\left(\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right)\right|=1+\left|\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right|=1+8=9\quad. -\] +Nevertheless, the formula $\left|B\right|^{\left|A\right|}$ is useful +since it shows the number of distinct functions that are possible +in principle. When types $A$ and $B$ have only a small number of +distinct values (for example, $A=$ \lstinline!Option[Boolean]]! +and $B=$ \lstinline!Either[Boolean, Boolean]!), the formula $\left|B\right|^{\left|A\right|}$ +gives an exact and practically relevant answer. +Let us now look for logic identities and arithmetic identities involving +function types. Table~\ref{tab:Logical-identities-with-function-types} +lists the available identities and the corresponding type equivalences. +(In the last column, we defined $a\triangleq\left|A\right|$, $b\triangleq\left|B\right|$, +and $c\triangleq\left|C\right|$ for brevity.) -\subsubsection{Example \label{subsec:ch-solvedExample-2}\ref{subsec:ch-solvedExample-2}} +It is notable that no logic identity is available for the formula +$\alpha\Rightarrow\left(\beta\vee\gamma\right)$, and correspondingly +no type equivalence is available for the type expression $A\rightarrow B+C$ +(although there is an identity for $A\rightarrow B\times C$). Reasoning +about types of the form $A\rightarrow B+C$ is more complicated because +those types usually cannot be transformed into simpler types. -Implement a Scala type \lstinline!P[A]! given by this type notation: -\[ -P^{A}\triangleq1+A+\text{Int}\times A+(\text{String}\rightarrow A)\quad. -\] +\begin{table} +\begin{centering} +\begin{tabular}{|c|c|c|} +\hline +\textbf{\footnotesize{}Logical identity (if holds)} & \textbf{\footnotesize{}Type equivalence} & \textbf{\footnotesize{}Arithmetic identity}\tabularnewline +\hline +\hline +{\footnotesize{}$\left(True\Rightarrow\alpha\right)=\alpha$} & {\footnotesize{}$\bbnum 1\rightarrow A\cong A$} & {\footnotesize{}$a^{1}=a$}\tabularnewline +\hline +{\footnotesize{}$\left(False\Rightarrow\alpha\right)=True$} & {\footnotesize{}$\bbnum 0\rightarrow A\cong\bbnum 1$} & {\footnotesize{}$a^{0}=1$}\tabularnewline +\hline +{\footnotesize{}$\left(\alpha\Rightarrow True\right)=True$} & {\footnotesize{}$A\rightarrow\bbnum 1\cong\bbnum 1$} & {\footnotesize{}$1^{a}=1$}\tabularnewline +\hline +{\footnotesize{}$\left(\alpha\Rightarrow False\right)\neq False$} & {\footnotesize{}$A\rightarrow\bbnum 0\not\cong\bbnum 0$} & {\footnotesize{}$0^{a}\neq0$}\tabularnewline +\hline +{\footnotesize{}$\left(\alpha\vee\beta\right)\Rightarrow\gamma=\left(\alpha\Rightarrow\gamma\right)\wedge\left(\beta\Rightarrow\gamma\right)$} & {\footnotesize{}$A+B\rightarrow C\cong\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)$} & {\footnotesize{}$c^{a+b}=c^{a}\times c^{b}$}\tabularnewline +\hline +{\footnotesize{}$(\alpha\wedge\beta)\Rightarrow\gamma=\alpha\Rightarrow\left(\beta\Rightarrow\gamma\right)$} & {\footnotesize{}$A\times B\rightarrow C\cong A\rightarrow B\rightarrow C$} & {\footnotesize{}$c^{a\times b}=(c^{b})^{a}$}\tabularnewline +\hline +{\footnotesize{}$\alpha\Rightarrow\left(\beta\wedge\gamma\right)=\left(\alpha\Rightarrow\beta\right)\wedge\left(\alpha\Rightarrow\gamma\right)$} & {\footnotesize{}$A\rightarrow B\times C\cong\left(A\rightarrow B\right)\times\left(A\rightarrow C\right)$} & {\footnotesize{}$\left(b\times c\right)^{a}=b^{a}\times c^{a}$}\tabularnewline +\hline +\end{tabular} +\par\end{centering} +\caption{Logical identities with implication, and the corresponding type equivalences +and arithmetic identities.\label{tab:Logical-identities-with-function-types}} +\end{table} + +We will now prove some of the type identities in Table~\ref{tab:Logical-identities-with-function-types}. + +\subsubsection{Example \label{subsec:ch-Example-type-identity-f}\ref{subsec:ch-Example-type-identity-f}\index{examples (with code)}} +Verify the type equivalence $\bbnum 1\rightarrow A\cong A$. \subparagraph{Solution} -To translate type notation into Scala code, begin by defining the -disjunctive types as case classes, choosing class names for convenience. -In this case, $P^{A}$ is a disjunctive type with four parts, so we -need four case classes: +Recall that the type notation $\bbnum 1\rightarrow A$ means the Scala +function type \lstinline!Unit => A!. There is only one value of type +\lstinline!Unit!. The choice of a function of type \lstinline!Unit => A! +is the same as the choice of a value of type \lstinline!A!. So, the +type $\bbnum 1\rightarrow A$ has $\left|A\right|$ distinct values, +and the arithmetic identity holds. + +To verify the type equivalence explicitly, we need to implement two +functions: \begin{lstlisting} -sealed trait P[A] -final case class P1[A](???) extends P[A] -final case class P2[A](???) extends P[A] -final case class P3[A](???) extends P[A] -final case class P4[A](???) extends P[A] +def f1[A]: (Unit => A) => A = ??? +def f2[A]: A => Unit => A = ??? \end{lstlisting} -Each of the case classes represents one part of the disjunctive type. -Now we write the contents for each of the case classes, in order to -implement the data in each of the disjunctive parts: +The first function needs to produce a value of type \lstinline!A!, +given an argument of the function type \lstinline!Unit => A!. The +only possibility is to apply that function to the value of type \lstinline!Unit!. +We can always produce that value as \lstinline!()!: \begin{lstlisting} -sealed trait P[A] -final case class P1[A]() extends P[A] -final case class P2[A](x: A) extends P[A] -final case class P3[A](n: Int, x: A) extends P[A] -final case class P4[A](f: String => A) extends P[A] +def f1[A]: (Unit => A) => A = (h: Unit => A) => h(()) \end{lstlisting} - - -\subsubsection{Example \label{subsec:ch-solvedExample-2a}\ref{subsec:ch-solvedExample-2a}} - -Find an equivalent disjunctive type for the type \lstinline!P = (Either[A, B], Either[C, D])!. - -\subparagraph{Solution} - -Begin by writing the given type in the type notation. The tuple becomes -the product type, and \lstinline!Either! becomes the disjunctive -(or \textsf{``}sum\textsf{''}) type: -\[ -P\triangleq(A+B)\times(C+D)\quad. -\] -By the usual rules of arithmetic, we expand brackets and obtain an -equivalent type: -\[ -P\cong A\times C+A\times D+B\times C+B\times D\quad. -\] -This is a disjunctive type having $4$ parts. - -\subsubsection{Example \label{subsec:ch-solvedExample-3}\ref{subsec:ch-solvedExample-3}} - -Show that the following type equivalences do \emph{not} hold: $A+A\not\cong A$ -and $A\times A\not\cong A$, although the corresponding logical identities -hold. - -\subparagraph{Solution} - -Note that the arithmetic equalities do not hold, $A+A\neq A$ and -$A\times A\ne A$. This already indicates that the types are not equivalent. -To build further intuition, consider that a value of type $A+A$ (in -Scala, \lstinline!Either[A, A]!) is a \lstinline!Left(a)! or a \lstinline!Right(a)! -for some \lstinline!a:A!. In the code notation, it is either $a^{:A}+\bbnum 0$ -or $\bbnum 0+a^{:A}$. So, a value of type $A+A$ contains a value -of type $A$ with the additional information about whether it is the -first or the second part of the disjunctive type. We cannot represent -that information in a single value of type $A$. - -Similarly, a value of type $A\times A$ contains two (possibly different) -values of type $A$, which cannot be represented by a single value -of type $A$ without loss of information. - -However, the corresponding logical identities $\alpha\vee\alpha=\alpha$ -and $\alpha\wedge\alpha=\alpha$ hold. To see that, we could derive -the four formulas: -\[ -\alpha\vee\alpha\Rightarrow\alpha\quad,\quad\quad\alpha\Rightarrow\alpha\vee\alpha\quad,\quad\quad\alpha\wedge\alpha\Rightarrow\alpha\quad,\quad\quad\alpha\Rightarrow\alpha\wedge\alpha\quad, -\] -using the proof rules of Section~\ref{subsec:The-rules-of-proof}. -Alternatively, we may use the CH correspondence and show that the -type signatures: -\[ -\forall A.\,A+A\rightarrow A\quad,\quad\quad\forall A.\,A\rightarrow A+A\quad,\quad\quad\forall A.\,A\times A\rightarrow A\quad,\quad\quad\forall A.\,A\rightarrow A\times A\quad -\] -can be implemented via fully parametric functions. For a programmer, -it is easier to write code than to guess the correct sequence of proof -rules. For the first pair of type signatures, we find: +Implementing \lstinline!f2! is straightforward. We can just discard +the \lstinline!Unit! argument: \begin{lstlisting} -def f1[A]: Either[A, A] => A = { - case Left(a) => a // No other choice here. - case Right(a) => a // No other choice here. -} -def f2[A]: A => Either[A, A] = { a => Left(a) } // Can be also Right(a). +def f2[A]: A => Unit => A = (x: A) => _ => x \end{lstlisting} -The presence of an arbitrary choice, to return \lstinline!Left(a)! -or \lstinline!Right(a)!, is a warning sign showing that additional -information is required to create a value of type \lstinline!Either[A, A]!. -This is precisely the information present in the type $A+A$ but missing -in the type $A$. - -The code notation for these functions is: -\[ -f_{1}\triangleq\,\begin{array}{|c||c|} - & A\\ -\hline A & a^{:A}\rightarrow a\\ -A & a^{:A}\rightarrow a -\end{array}\,=\,\begin{array}{|c||c|} - & A\\ -\hline A & \text{id}\\ -A & \text{id} -\end{array}\quad,\quad\quad f_{2}\triangleq a^{:A}\rightarrow a+\bbnum 0^{:A}=\,\begin{array}{|c||cc|} - & A & A\\ -\hline A & a^{:A}\rightarrow a & \bbnum 0 -\end{array}\,=\,\begin{array}{|c||cc|} - & A & A\\ -\hline A & \text{id} & \bbnum 0 -\end{array}\quad. -\] -The composition of these functions is not equal to identity: -\[ -f_{1}\bef f_{2}=\,\begin{array}{||c|} -\text{id}\\ -\text{id} -\end{array}\,\bef\,\begin{array}{||cc|} -\text{id} & \bbnum 0\end{array}\,=\,\begin{array}{||cc|} -\text{id} & \bbnum 0\\ -\text{id} & \bbnum 0 -\end{array}\,\quad,\quad\text{while we have}\quad\text{id}^{:A+A\rightarrow A+A}=\,\begin{array}{||cc|} -\text{id} & \bbnum 0\\ -\bbnum 0 & \text{id} -\end{array}\quad. -\] +It remains to show that the functions \lstinline!f1! and \lstinline!f2! +are inverses of each other. Let us show the proof using Scala code +and then using the code notation. -For the second pair of type signatures, the code is: +Writing Scala code, compute \lstinline!f1(f2(x))! for an arbitrary +\lstinline!x:A!. Using the code of \lstinline!f1! and \lstinline!f2!, +we get: \begin{lstlisting} -def f1[A]: ((A, A)) => A = { case (a1, a2) => a1 } // Could be also `a2`. -cef f2[A]: A => (A, A) = { a => (a, a) } // No other choice here. +f1(f2(x)) == f1(_ => x) == (_ => x)(()) == x \end{lstlisting} -It is clear that the first function loses information when it returns -\lstinline!a1! and discards \lstinline!a2! (or vice versa). +Now compute \lstinline!f2(f1(h))! for arbitrary \lstinline!h: Unit => A! +in Scala code: +\begin{lstlisting} +f2(f1(h)) == f2(h(())) == { _ => h(()) } +\end{lstlisting} +How can we show that the function \lstinline!{_ => h(())}! is equal +to \lstinline!h!? Whenever we apply equal functions to equal arguments, +they return equal results. In our case, the argument of \lstinline!h! +is of type \lstinline!Unit!, so we only need to verify that the result +of applying \lstinline!h! to the value \lstinline!()! is the same +as the result of applying \lstinline!{_ => h(())}! to \lstinline!()!. +In other words, we need to apply both sides to an additional argument +\lstinline!()!: +\begin{lstlisting} +f2(f1(h))(()) == { _ => h(()) } (()) == h(()) +\end{lstlisting} +This completes the proof. -The code notation for the functions \lstinline!f1! and \lstinline!f2! -is: +For comparison, let us show the same proof in the code notation. The +functions $f_{1}$ and $f_{2}$ are: \[ -f_{1}\triangleq a_{1}^{:A}\times a_{2}^{:A}\rightarrow a_{1}=\pi_{1}^{:A\times A\rightarrow A}\quad,\quad\quad f_{2}\triangleq a^{:A}\rightarrow a\times a=\Delta^{:A\rightarrow A\times A}\quad. +f_{1}\triangleq h^{:\bbnum 1\rightarrow A}\rightarrow h(1)\quad,\quad\quad f_{2}\triangleq x^{:A}\rightarrow1\rightarrow x\quad. \] -Computing the compositions of these functions, we find that $f_{2}\bef f_{1}=\text{id}$ -while $f_{1}\bef f_{2}\ne\text{id}$: +Now write the function compositions in both directions: \begin{align*} -f_{1}\bef f_{2} & =\left(a_{1}\times a_{2}\rightarrow a_{1}\right)\bef\left(a\rightarrow a\times a\right)\\ - & =\left(a_{1}\times a_{2}\rightarrow a_{1}\times a_{1}\right)\neq\text{id}=\left(a_{1}\times a_{2}\rightarrow a_{1}\times a_{2}\right)\quad. +{\color{greenunder}\text{expect to equal }\text{id}:}\quad & f_{1}\bef f_{2}=(h^{:\bbnum 1\rightarrow A}\rightarrow h(1))\bef(x^{:A}\rightarrow1\rightarrow x)\\ +{\color{greenunder}\text{compute composition}:}\quad & \quad=h^{:\bbnum 1\rightarrow A}\rightarrow\gunderline{1\rightarrow h(1)}\\ +{\color{greenunder}\text{note that }1\rightarrow h(1)\text{ is the same as }h:}\quad & \quad=(h^{:\bbnum 1\rightarrow A}\rightarrow h)=\text{id}\quad. +\end{align*} +\begin{align*} +{\color{greenunder}\text{expect to equal }\text{id}:}\quad & f_{2}\bef f_{1}=(x^{:A}\rightarrow1\rightarrow x)\bef(h^{:\bbnum 1\rightarrow A}\rightarrow h(1))\\ +{\color{greenunder}\text{compute composition}:}\quad & \quad=x^{:A}\rightarrow\gunderline{(1\rightarrow x)(1)}\\ +{\color{greenunder}\text{apply function}:}\quad & \quad=(x^{:A}\rightarrow x)=\text{id}\quad. \end{align*} -We have implemented all four type signatures as fully parametric functions, -which shows that the corresponding logical formulas are all true (i.e., -can be derived using the proof rules). However, the functions cannot -be inverses of each other. So, the type equivalences do not hold. +The type $\bbnum 1\rightarrow A$ is equivalent to the type $A$ in +the sense of carrying the same information, but these types are not +exactly the same. An important difference between these types is that +a value of type $A$ is available immediately, while a value of type +$\bbnum 1\rightarrow A$ is a function that still needs to be applied +to an argument (of type $\bbnum 1$) before a value of type $A$ is +obtained. The type $\bbnum 1\rightarrow A$ may represent an \textsf{``}on-call\textsf{''}\index{on-call value} +value of type $A$. That value is computed on demand every time it +is requested. (See Section~\ref{subsec:Lazy-values-iterators-and-streams} +for more details about \textsf{``}on-call\textsf{''} values.) -\subsubsection{Example \label{subsec:ch-solvedExample-4}\ref{subsec:ch-solvedExample-4}} +The void type\index{void type} $\bbnum 0$ needs special reasoning, +as the next examples show: -Show that $\left(\left(A\wedge B\right)\Rightarrow C\right)\neq(A\Rightarrow C)\vee(B\Rightarrow C)$ -in the constructive logic, but the equality holds in Boolean logic. -This is another example where the Boolean reasoning fails to give -correct answers about implementability of type signatures. +\subsubsection{Example \label{subsec:ch-Example-type-identity-0-to-A}\ref{subsec:ch-Example-type-identity-0-to-A}} + +Verify the type equivalence $\bbnum 0\rightarrow A\cong\bbnum 1$. \subparagraph{Solution} -Begin by rewriting the logical equality as two implications: -\[ -(A\wedge B\Rightarrow C)\Rightarrow(A\Rightarrow C)\vee(B\Rightarrow C)\quad\text{ and }\quad\left((A\Rightarrow C)\vee(B\Rightarrow C)\right)\Rightarrow\left(\left(A\wedge B\right)\Rightarrow C\right)\quad. -\] -It is sufficient to show that one of these implications is incorrect. -Rather than looking for a proof tree in the constructive logic (which -would be difficult, since we need to demonstrate that \emph{no} proof -exists), let us use the CH correspondence. According to the CH correspondence, -an equivalent task is to implement fully parametric functions with -the type signatures: -\[ -(A\times B\rightarrow C)\rightarrow(A\rightarrow C)+(B\rightarrow C)\quad\text{ and }\quad(A\rightarrow C)+(B\rightarrow C)\rightarrow A\times B\rightarrow C\quad. -\] -For the first type signature, the Scala code is: +To verify that a type $X$ is equivalent to the \lstinline!Unit! +type, we need to show that there is only one distinct value of type +$X$. So, let us find out how many values the type $\bbnum 0\rightarrow A$ +has. Consider a value of that type, which is a function $f^{:\bbnum 0\rightarrow A}$ +from the type $\bbnum 0$ to a type $A$. Since there exist no values +of type $\bbnum 0$, the function $f$ will never be applied to any +arguments and so \emph{does not need} to compute any actual values +of type $A$. So, $f$ is a function whose body may be \textsf{``}empty\textsf{''}. +At least, $f$\textsf{'}s body does not need to contain any expressions of +type $A$. In Scala, such a function can be written as: \begin{lstlisting} -def f1[A,B,C]: (((A, B)) => C) => Either[A => C, B => C] = { k => ??? } +def absurd[A]: Nothing => A = { ??? } \end{lstlisting} -We are required to return either a \lstinline!Left(g)! with \lstinline!g: A => C!, -or a \lstinline!Right(h)! with \lstinline!h: B => C!. The only given -data is a function \lstinline!k! of type $A\times B\rightarrow C$, -so the decision of whether to return a \lstinline!Left! or a \lstinline!Right! -must be hard-coded in the function \lstinline!f1! independently of -\lstinline!k!. Can we produce a function \lstinline!g! of type \lstinline!A => C!? -Given a value of type \lstinline!A!, we would need to return a value -of type \lstinline!C!. The only way to obtain a value of type \lstinline!C! -is by applying \lstinline!k! to some arguments. But to apply \lstinline!k!, -we need a value of type \lstinline!B!, which we do not have. So we -cannot produce a \lstinline!g: A => C!. Similarly, we cannot produce -a function \lstinline!h! of type \lstinline!B => C!. - -We repeat the same argument in the type notation. Obtaining a value -of type $(A\rightarrow C)+(B\rightarrow C)$ means to compute either -$g^{:A\rightarrow C}+\bbnum 0$ or $\bbnum 0+h^{:B\rightarrow C}$. -This decision must be hard-coded since the only data is a function -$k^{:A\times B\rightarrow C}$. We can compute $g^{:A\rightarrow C}$ -only by partially applying $k^{:A\times B\rightarrow C}$ to a value -of type $B$. However, we have no values of type $B$. Similarly, -we cannot compute a value $h^{:B\rightarrow C}$. - -The inverse type signature \emph{can} be implemented: - -\begin{wrapfigure}{l}{0.55\columnwidth}% -\vspace{0.32\baselineskip} +This code will compile without type errors. An equivalent code is: \begin{lstlisting} -def f2[A,B,C]: Either[A=>C, B=>C] => ((A,B)) => C = { - case Left(g) => { case (a, b) => g(a) } - case Right(h) => { case (a, b) => h(b) } -} +def absurd[A]: Nothing => A = { x => ??? } +\end{lstlisting} +The symbol \lstinline!???! is defined in the Scala library and represents +code that is \textsf{``}not implemented\textsf{''}. Trying to evaluate this symbol +will produce an error: +\begin{lstlisting} +scala> val x = ??? +scala.NotImplementedError: an implementation is missing \end{lstlisting} +Since the function \lstinline!absurd! can never be applied to an +argument, this error will never happen. So, one can pretend that the +result value (which will never be computed) has any required type, +e.g., the type $A$. In this way, the compiler will accept the definition +of \lstinline!absurd!. -\vspace{-2.4\baselineskip} -\end{wrapfigure}% -\vspace{-1.2\baselineskip} +Let us now verify that there exists \emph{only one} distinct function +of type $\bbnum 0\rightarrow A$. Take any two functions of that type, +$f^{:\bbnum 0\rightarrow A}$ and $g^{:\bbnum 0\rightarrow A}$. Are +they different? The only way of showing that $f$ and $g$ are different +is by finding a value $x$ such that $f(x)\neq g(x)$. But then $x$ +would be of type $\bbnum 0$, and there are \emph{no} \emph{values} +of type $\bbnum 0$. So, we will never be able to find the required +value $x$. It follows that any two functions $f$ and $g$ of type +$\bbnum 0\rightarrow A$ are equal, $f=g$. In other words, there +exists only one distinct value of type $\bbnum 0\rightarrow A$. Since +the cardinality of the type $\bbnum 0\rightarrow A$ is $1$, we obtain +the type equivalence $\bbnum 0\rightarrow A\cong\bbnum 1$. -\noindent -\[ -f_{2}\triangleq\,\begin{array}{|c||c|} - & A\times B\rightarrow C\\ -\hline A\rightarrow C & g^{:A\rightarrow C}\rightarrow a\times b\rightarrow g(a)\\ -B\rightarrow C & h^{:B\rightarrow C}\rightarrow a\times b\rightarrow h(b) -\end{array}\quad. -\] -\vspace{-0.9\baselineskip} +\subsubsection{Example \label{subsec:ch-Example-type-identity-A-0}\ref{subsec:ch-Example-type-identity-A-0}} -Let us now show that the logical identity: -\begin{equation} -((\alpha\wedge\beta)\Rightarrow\gamma)=((\alpha\Rightarrow\gamma)\vee(\beta\Rightarrow\gamma))\label{eq:ch-example-identity-boolean-not-constructive} -\end{equation} -holds in Boolean logic. A straightforward calculation is to simplify -the Boolean expression using Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic}), -which only holds in Boolean logic (but not in the constructive logic). -We find: -\begin{align*} -{\color{greenunder}\text{left-hand side of Eq.~(\ref{eq:ch-example-identity-boolean-not-constructive})}:}\quad & \left(\alpha\wedge\beta\right)\gunderline{\Rightarrow}\,\gamma\\ -{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & \quad=\gunderline{\neg(\alpha\wedge\beta)}\vee\gamma\\ -{\color{greenunder}\text{use de Morgan\textsf{'}s law}:}\quad & \quad=\neg\alpha\vee\neg\beta\vee\gamma\quad.\\ -{\color{greenunder}\text{right-hand side of Eq.~(\ref{eq:ch-example-identity-boolean-not-constructive})}:}\quad & (\gunderline{\alpha\Rightarrow\gamma})\vee(\gunderline{\beta\Rightarrow\gamma})\\ -{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & \quad=\neg\alpha\vee\gunderline{\gamma}\vee\neg\beta\vee\gunderline{\gamma}\\ -{\color{greenunder}\text{use identity }\gamma\vee\gamma=\gamma:}\quad & \quad=\neg\alpha\vee\neg\beta\vee\gamma\quad. -\end{align*} -Both sides of Eq.~(\ref{eq:ch-example-identity-boolean-not-constructive}) -are equal to the same formula, $\neg\alpha\vee\neg\beta\vee\gamma$, -so the identity holds. +Show that $A\rightarrow\bbnum 0\not\cong\bbnum 0$ and $A\rightarrow\bbnum 0\not\cong\bbnum 1$, +where $A$ is an arbitrary type. -This calculation does not work in the constructive logic because its -proof rules can derive neither the Boolean formula~(\ref{eq:ch-definition-of-implication-in-Boolean-logic}) -nor the \textbf{law of de Morgan}\index{law of de Morgan}, $\neg(\alpha\wedge\beta)=\left(\neg\alpha\vee\neg\beta\right)$. +\subparagraph{Solution} -Another way of proving the Boolean identity~(\ref{eq:ch-example-identity-boolean-not-constructive}) -is to enumerate all possible truth values for the variables $\alpha$, -$\beta$, and $\gamma$. The left-hand side, $\left(\alpha\wedge\beta\right)\Rightarrow\gamma$, -can be $False$ only if $\alpha\wedge\beta=True$ (that is, both $\alpha$ -and $\beta$ are $True$) and $\gamma=False$. For all other truth -values of $\alpha$, $\beta$, and $\gamma$, the formula $\left(\alpha\wedge\beta\right)\Rightarrow\gamma$ -is $True$. Let us determine when the right-hand side, $(\alpha\Rightarrow\gamma)\vee(\beta\Rightarrow\gamma)$, -can be $False$. This can happen only if both parts of the disjunction -are $False$. That means $\alpha=True$, $\beta=True$, and $\gamma=False$. -So, the two sides of the identity~(\ref{eq:ch-example-identity-boolean-not-constructive}) -are both $True$ or both $False$ with any choice of truth values -of $\alpha$, $\beta$, and $\gamma$. In Boolean logic, this is sufficient -to prove the identity~(\ref{eq:ch-example-identity-boolean-not-constructive}). -$\square$ +To prove that two types are \emph{not} equivalent, it is sufficient +to show that their cardinalities are different. Let us determine the +cardinality of the type $A\rightarrow\bbnum 0$, assuming that the +cardinality of $A$ is known. We note that a function of type, say, +$\text{Int}\rightarrow\bbnum 0$ is impossible to implement. (If we +had such a function $f^{:\text{Int}\rightarrow\bbnum 0}$, we could +evaluate, say, $x\triangleq f(123)$ and obtain a value $x$ of type +$\bbnum 0$, which is impossible by definition of the type $\bbnum 0$. +It follows that $\left|\text{Int}\rightarrow\bbnum 0\right|=0$. However, +Example~\ref{subsec:ch-Example-type-identity-0-to-A} shows that +$\bbnum 0\rightarrow\bbnum 0$ has cardinality $1$. So, we find that +$\left|A\rightarrow\bbnum 0\right|=1$ if the type $A$ is itself +$\bbnum 0$ but $\left|A\rightarrow\bbnum 0\right|=0$ for all other +types $A$. We conclude that the type $A\rightarrow\bbnum 0$ is not +equivalent to $\bbnum 0$ or $\bbnum 1$ when $A$ is an unknown type. +The type $A\rightarrow\bbnum 0$ is void for non-void types $A$, +and vice versa. -The following example shows how to use the formulas from Tables~\ref{tab:Logical-identities-with-disjunction-and-conjunction}\textendash \ref{tab:Logical-identities-with-function-types} -to derive the type equivalence of complicated type expressions without -need for proofs. +\subsubsection{Example \label{subsec:ch-Example-type-identity-2}\ref{subsec:ch-Example-type-identity-2}} -\subsubsection{Example \label{subsec:ch-solvedExample-5-2}\ref{subsec:ch-solvedExample-5-2}} +Verify the type equivalence $A\rightarrow\bbnum 1\cong\bbnum 1$. -Use known formulas to verify the type equivalences without direct -proofs: +\subparagraph{Solution} -\textbf{(a)} $A\times\left(A+\bbnum 1\right)\times\left(A+\bbnum 1+\bbnum 1\right)\cong A\times\left(\bbnum 1+\bbnum 1+A\times\left(\bbnum 1+\bbnum 1+\bbnum 1+A\right)\right)$. +There is only one fully parametric function that returns $\bbnum 1$: +\begin{lstlisting} +def f[A]: A => Unit = { _ => () } +\end{lstlisting} +The function $f$ cannot use its argument of type $A$ since nothing +is known about the type $A$. So, the code of $f$ \emph{must} discard +its argument and return the fixed value \lstinline!()! of type \lstinline!Unit!. +In the code notation, this function is written as: +\[ +f^{:A\rightarrow\bbnum 1}\triangleq\_\rightarrow1\quad. +\] +We can show that there exists only \emph{one} distinct function of +type $A\rightarrow\bbnum 1$ (that is, the type $A\rightarrow\bbnum 1$ +has cardinality $1$). Assume that $f$ and $g$ are two such functions, +and try to find a value $x^{:A}$ such that $f(x)\neq g(x)$. We cannot +find any such $x$ because $f(x)=1$ and $g(x)=1$ for all $x$. So, +any two functions $f$ and $g$ of type $A\rightarrow\bbnum 1$ must +be equal to each other. The cardinality of the type $A\rightarrow\bbnum 1$ +is $1$. -\textbf{(b)} $\bbnum 1+A+B\rightarrow\bbnum 1\times B\cong\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B$. +Any type having cardinality $1$ is equivalent to the \lstinline!Unit! +type ($\bbnum 1$). So, $A\rightarrow\bbnum 1\cong\bbnum 1$. + +\subsubsection{Example \label{subsec:ch-Example-type-identity-6-1}\ref{subsec:ch-Example-type-identity-6-1}} + +Denote by $\_^{:A}\rightarrow B$ the type of \index{constant function}constant +functions of type $A\rightarrow B$ (functions that ignore their argument). +Show that the type $\_^{:A}\rightarrow B$ is equivalent to the type +$B$, as long as $A\neq\bbnum 0$. \subparagraph{Solution} -\textbf{(a)} We can expand brackets in type expressions as in arithmetic: +An isomorphism between the types $B$ and $\_^{:A}\rightarrow B$ +is given by the two functions: \begin{align*} -A\times\left(A+\bbnum 1\right) & \cong A\times A+A\times\bbnum 1\cong A\times A+A\quad,\\ -A\times\left(A+\bbnum 1\right)\times\left(A+\bbnum 1+\bbnum 1\right) & \cong\left(A\times A+A\right)\times\left(A+\bbnum 1+\bbnum 1\right)\\ - & \cong A\times A\times A+A\times A+A\times A\times\left(\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\\ - & \cong A\times A\times A+A\times A\times\left(\bbnum 1+\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\quad. + & f_{1}:B\rightarrow\_^{:A}\rightarrow B\quad,\quad\quad f_{1}\triangleq b\rightarrow\_\rightarrow b\quad;\\ + & f_{2}:(\_^{:A}\rightarrow B)\rightarrow B\quad,\quad\quad f_{2}\triangleq k^{:\_\rightarrow B}\rightarrow k(x^{:A})\quad, +\end{align*} +where $x$ is any value of type $A$. That value exists since the +type $A$ is not void. The function $f_{2}$ does not depend on the +choice of $x$ because $k$ is a constant function, so $k(x)$ is +the same for all $x$. In other words, the function $k$ satisfies +$k=(\_\rightarrow k(x))$ with any chosen $x$. To prove that $f_{1}$ +and $f_{2}$ are inverses: +\begin{align*} + & f_{1}\bef f_{2}=(b\rightarrow\_\rightarrow b)\bef(k\rightarrow k(x))=b\rightarrow(\_\rightarrow b)(x)=(b\rightarrow b)=\text{id}\quad,\\ + & f_{2}\bef f_{1}=(k\rightarrow k(x))\bef(b\rightarrow\_\rightarrow b)=k\rightarrow\_\rightarrow k(x)=k\rightarrow k=\text{id}\quad. \end{align*} -The result looks like a polynomial in $A$, which we can now rearrange -into the required form: -\[ -A\times A\times A+A\times A\times\left(\bbnum 1+\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\cong A\times\left(\bbnum 1+\bbnum 1+A\times\left(\bbnum 1+\bbnum 1+\bbnum 1+A\right)\right)\quad. -\] -\textbf{(b)} Keep in mind that the conventions of the type notation -make the function arrow $\left(\rightarrow\right)$ group weaker than -other type operations. So, the type expression $\bbnum 1+A+B\rightarrow\bbnum 1\times B$ -means a function from $\bbnum 1+A+B$ to $\bbnum 1\times B$. -Begin by using the equivalence $\bbnum 1\times B\cong B$ to obtain -$\bbnum 1+A+B\rightarrow B$. Now we use another rule: -\[ -A+B\rightarrow C\cong\left(A\rightarrow C\right)\times\left(B\rightarrow C\right) -\] -and derive the equivalence: -\[ -\bbnum 1+A+B\rightarrow B\cong\left(\bbnum 1\rightarrow B\right)\times\left(A\rightarrow B\right)\times\left(B\rightarrow B\right)\quad. -\] -Finally, we note that $\bbnum 1\rightarrow B\cong B$ and that the -type product is commutative, so we can rearrange the last type expression -into the required form: +\subsubsection{Example \label{subsec:ch-Example-type-identity-5}\ref{subsec:ch-Example-type-identity-5}} + +Verify the following type equivalence: \[ -B\times\left(A\rightarrow B\right)\times\left(B\rightarrow B\right)\cong\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B\quad. +A+B\rightarrow C\cong(A\rightarrow C)\times(B\rightarrow C)\quad. \] -We obtain the required type expression: $\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B$. - -\subsubsection{Example \label{subsec:ch-solvedExample-5}\ref{subsec:ch-solvedExample-5}} -Denote $\text{Read}^{E,A}\triangleq E\rightarrow A$ and implement -fully parametric functions with types $A\rightarrow\text{Read}^{E,A}$ -and $\text{Read}^{E,A}\rightarrow(A\rightarrow B)\rightarrow\text{Read}^{E,B}$. \subparagraph{Solution} -Begin by defining a type alias for the type constructor $\text{Read}^{E,A}$: -\begin{lstlisting} -type Read[E, A] = E => A -\end{lstlisting} -The first type signature has only one implementation: +Begin by implementing two functions with type signatures: \begin{lstlisting} -def p[E, A]: A => Read[E, A] = { x => _ => x } +def f1[A,B,C]: (Either[A, B] => C) => (A => C, B => C) = ??? +def f2[A,B,C]: ((A => C, B => C)) => Either[A, B] => C = ??? \end{lstlisting} -We \emph{must} discard the argument of type $E$; we cannot use it -for computing a value of type \lstinline!A! given \lstinline!x:A!. - -The second type signature has three type parameters. It is the curried -version of the function \lstinline!map!: +The code can be derived unambiguously from the type signatures. For +the first function, we need to produce a pair of functions of type +\lstinline!(A => C, B => C)!. Can we produce the first part of that +pair? Computing a function of type \lstinline!A => C! means that +we need to produce a value of type \lstinline!C! given an arbitrary +value \lstinline!a:A!. The available data is a function of type \lstinline!Either[A, B] => C! +called, say, \lstinline!h!. We can apply that function to \lstinline!Left(a)! +and obtain a value of type \lstinline!C! as required. So, a function +of type \lstinline!A => C! is computed as \lstinline!a => h(Left(a))!. +We can produce a function of type \lstinline!B => C! similarly. The +code is: \begin{lstlisting} -def map[E, A, B]: Read[E, A] => (A => B) => Read[E, B] = ??? +def f1[A,B,C]: (Either[A, B] => C) => (A => C, B => C) = + (h: Either[A, B] => C) => (a => h(Left(a)), b => h(Right(b))) \end{lstlisting} -Expanding the type alias, we see that the two curried arguments are -functions of types $E\rightarrow A$ and $A\rightarrow B$. The forward -composition of these functions is a function of type $E\rightarrow B$, -or $\text{Read}^{E,B}$, which is exactly what we are required to -return. So, the code can be written as: +We write this function in the code notation like this: +\begin{align*} + & f_{1}:\left(A+B\rightarrow C\right)\rightarrow\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)\quad,\\ + & f_{1}\triangleq h^{:A+B\rightarrow C}\rightarrow\big(a^{:A}\rightarrow h(a+\bbnum 0^{:B})\big)\times\big(b^{:B}\rightarrow h(\bbnum 0^{:A}+b)\big)\quad. +\end{align*} +For the function \lstinline!f2!, we need to apply pattern matching +to both curried arguments and then return a value of type \lstinline!C!. +This can be achieved in only one way: \begin{lstlisting} -def map[E, A, B]: (E => A) => (A => B) => E => B = { r => f => r andThen f } +def f2[A,B,C](f: A => C, g: B => C): Either[A, B] => C = { + case Left(a) => f(a) + case Right(b) => g(b) +} \end{lstlisting} -If we did not notice this shortcut, we would reason differently: We -are required to compute a value of type $B$ given \emph{three} curried -arguments $r^{:E\rightarrow A}$, $f^{:A\rightarrow B}$, and $e^{:E}$. -Write this requirement as: +We write this function in the code notation like this: +\begin{align*} + & f_{2}:\left(A\rightarrow C\right)\times\left(B\rightarrow C\right)\rightarrow A+B\rightarrow C\quad,\\ + & f_{2}\triangleq f^{:A\rightarrow C}\times g^{:B\rightarrow C}\rightarrow\,\begin{array}{|c||c|} + & C\\ +\hline A & a\rightarrow f(a)\\ +B & b\rightarrow g(b) +\end{array}\quad. +\end{align*} +The matrix in the last line has only one column because the result +type, $C$, is not known to be a disjunctive type. We may also simplify +the functions, e.g., replace $a\rightarrow f(a)$ by just $f$, and +write: \[ -\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow???^{:B}\quad, +f_{2}\triangleq f^{:A\rightarrow C}\times g^{:B\rightarrow C}\rightarrow\,\begin{array}{|c||c|} + & C\\ +\hline A & f\\ +B & g +\end{array}\quad. \] -The symbol $\text{???}^{:B}$ is called a \index{typed hole}\textbf{typed -hole}. It stands for a value that we are still figuring out how to -compute, but whose type is already known. Typed holes are supported -in Scala by an experimental compiler plugin.\footnote{See \texttt{\href{https://github.com/cb372/scala-typed-holes}{https://github.com/cb372/scala-typed-holes}}} -The plugin will print the known information about the typed hole. -To fill the typed hole $\text{???}^{:B}$, we need a value of type -$B$. Since no arguments have type $B$, the only way of getting a -value of type $B$ is to apply $f^{:A\rightarrow B}$ to some value -of type $A$. So we write: +It remains to verify that $f_{1}\bef f_{2}=\text{id}$ and $f_{2}\bef f_{1}=\text{id}$. +To compute $f_{1}\bef f_{2}$, we write (omitting types): +\begin{align*} +f_{1}\bef f_{2}= & \big(h\rightarrow(a\rightarrow h(a+\bbnum 0))\times(b\rightarrow h(\bbnum 0+b))\big)\bef\bigg(f\times g\rightarrow\,\begin{array}{||c|} +f\\ +g +\end{array}\,\bigg)\\ +{\color{greenunder}\text{compute composition}:}\quad & =h\rightarrow\,\begin{array}{||c|} +a\rightarrow h(a+\bbnum 0)\\ +b\rightarrow h(\bbnum 0+b) +\end{array}\quad. +\end{align*} +To proceed, we need to simplify the expressions $h(a+\bbnum 0)$ and +$h(\bbnum 0+b)$. We rewrite the argument $h$ (an arbitrary function +of type $A+B\rightarrow C$) in the matrix notation: \[ -\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(???^{:A})\quad. +h\triangleq\,\begin{array}{|c||c|} + & C\\ +\hline A & a\rightarrow p(a)\\ +B & b\rightarrow q(b) +\end{array}\,=\,\begin{array}{|c||c|} + & C\\ +\hline A & p\\ +B & q +\end{array}\quad, \] -The only way of getting an $A$ is to apply $r$ to some value of -type $E$: +where $p^{:A\rightarrow C}$ and $q^{:B\rightarrow C}$ are new arbitrary +functions. Since we already checked the types, we can omit all type +annotations and write $h$ as: \[ -\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(r(???^{:E}))\quad. +h\triangleq\,\begin{array}{||c|} +p\\ +q +\end{array}\quad. \] -We have exactly one value of type $E$, namely $e^{:E}$. So, the -code must be: +To evaluate expressions such as $h(a+\bbnum 0)$ and $h(\bbnum 0+b)$, +we need to use one of the rows of this matrix. The correct row will +be selected \emph{automatically} by the rules of matrix multiplication +if we place a row vector to the left of the matrix and use the convention +of omitting terms containing $\bbnum 0$: \[ -\text{map}^{E,A,B}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(r(e))\quad. +\begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array}\,=a\triangleright p\quad,\quad\quad\begin{array}{|cc|} +\bbnum 0 & b\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array}\,=b\triangleright q\quad. \] -Translate this to the Scala syntax: -\begin{lstlisting} -def map[E, A, B]: (E => A) => (A => B) => E => B = { r => f => e => f(r(e)) } -\end{lstlisting} -We may now notice that the expression $e\rightarrow f(r(e))$ is a -function composition $r\bef f$ applied to $e$, and simplify the -code accordingly. +Here we used the symbol $\triangleright$ to separate an argument +from a function when the argument is written to the \emph{left} of +the function. The symbol $\triangleright$ (pronounced \textsf{``}pipe\textsf{''}\index{pipe notation}\index{triangleright-notation@$\triangleright$-notation!see \textsf{``}pipe notation\textsf{''}}) +is defined by $x\triangleright f\triangleq f(x)$. In Scala, this +operation is available as \lstinline!x.pipe(f)! as of Scala 2.13. -\subsubsection{Example \label{subsec:ch-solvedExample-6}\ref{subsec:ch-solvedExample-6}} +We can write values of disjunctive types, such as $a+\bbnum 0$, as +row vectors $\,\begin{array}{|cc|} +a & \bbnum 0\end{array}\,$: +\begin{equation} +h(a+\bbnum 0)=(a+\bbnum 0)\triangleright h=\,\begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright\,h\quad.\label{eq:forward-notation-} +\end{equation} +With these notations, we compute further. Omit all terms applying +$\bbnum 0$ or applying something to $\bbnum 0$: +\begin{align*} + & \begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright\,h=\,\begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array}\,=a\triangleright p=p(a)\quad,\\ + & \begin{array}{|cc|} +\bbnum 0 & b\end{array}\,\triangleright\,h=\,\,\begin{array}{|cc|} +\bbnum 0 & b\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array}\,=b\triangleright q=q(b)\quad. +\end{align*} +Now we can complete the proof of $f_{1}\bef f_{2}=\text{id}$: +\begin{align*} +f_{1}\bef f_{2} & =h\rightarrow\,\begin{array}{||c|} +a\rightarrow h(a+\bbnum 0)\\ +b\rightarrow h(\bbnum 0+b) +\end{array}\\ +{\color{greenunder}\text{previous equations}:}\quad & =\,\begin{array}{||c|} +p\\ +q +\end{array}\,\rightarrow\,\begin{array}{||c|} +a\rightarrow p(a)\\ +b\rightarrow q(b) +\end{array}\\ +{\color{greenunder}\text{simplify functions}:}\quad & =\,\,\begin{array}{||c|} +p\\ +q +\end{array}\,\rightarrow\,\begin{array}{||c|} +p\\ +q +\end{array}\,=\text{id}\quad. +\end{align*} -Show that the type signature \lstinline!Read[A, T] => (A => B) => Read[B, T]! -cannot be implemented as a fully parametric function. +To prove that $f_{2}\bef f_{1}=\text{id}$, use the notation~(\ref{eq:forward-notation-}): +\begin{align*} +f_{2}\bef f_{1}= & \bigg(f\times g\rightarrow\,\begin{array}{||c|} +f\\ +g +\end{array}\,\bigg)\bef\big(h\rightarrow(a\rightarrow(a+\bbnum 0)\triangleright h)\times(b\rightarrow(\bbnum 0+b)\triangleright h)\big)\\ +{\color{greenunder}\text{composition}:}\quad & =f\times g\rightarrow\big(a\rightarrow\gunderline{\,\begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright}\,\begin{array}{||c|} +f\\ +g +\end{array}\,\big)\times\big(b\rightarrow\gunderline{\,\begin{array}{|cc|} +\bbnum 0 & b\end{array}\,\triangleright}\,\begin{array}{||c|} +f\\ +g +\end{array}\,\big)\\ +{\color{greenunder}\text{apply functions}:}\quad & =f\times g\rightarrow(a\rightarrow\gunderline{a\triangleright f})\times(b\rightarrow\gunderline{b\triangleright g})\\ +{\color{greenunder}\text{definition of }\triangleright:}\quad & =f\times g\rightarrow\gunderline{\left(a\rightarrow f(a)\right)}\times\gunderline{\left(b\rightarrow g(b)\right)}\\ +{\color{greenunder}\text{simplify functions}:}\quad & =\left(f\times g\rightarrow f\times g\right)=\text{id}\quad. +\end{align*} -\subparagraph{Solution} +In this way, we have proved that $f_{1}$ and $f_{2}$ are mutual +inverses. The proofs appear long because we took time to motivate +and introduce new notation for applying matrices to row vectors. Once +this notation is understood, the proof for $f_{1}\bef f_{2}=\text{id}$ +can be written as: +\begin{align*} +f_{1}\bef f_{2}= & \left(h\rightarrow(a\rightarrow(a+\bbnum 0)\triangleright h)\times(b\rightarrow(\bbnum 0+b)\triangleright h)\right)\bef\bigg(f\times g\rightarrow\,\begin{array}{||c|} +f\\ +g +\end{array}\bigg)\\ +{\color{greenunder}\text{composition}:}\quad & =h\rightarrow\,\begin{array}{||c|} +\,a\,\rightarrow\,\left|\begin{array}{cc} +a & \bbnum 0\end{array}\right|\triangleright h\\ +b\rightarrow\left|\begin{array}{cc} +\bbnum 0 & b\end{array}\right|\triangleright h +\end{array}\,=\,\begin{array}{||c|} +p\\ +q +\end{array}\rightarrow\,\begin{array}{||c|} +a\rightarrow\,\begin{array}{|cc|} +a & \bbnum 0\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array}\\ +b\,\rightarrow\,\begin{array}{|cc|} +\bbnum 0 & b\,\,\end{array}\,\triangleright\,\begin{array}{||c|} +p\\ +q +\end{array} +\end{array}\\ +{\color{greenunder}\text{apply functions}:}\quad & =\,\begin{array}{||c|} +p\\ +q +\end{array}\,\rightarrow\,\begin{array}{||c|} +a\rightarrow a\triangleright p\\ +b\rightarrow b\triangleright q +\end{array}\,=\,\begin{array}{||c|} +p\\ +q +\end{array}\,\rightarrow\,\begin{array}{||c|} +p\\ +q +\end{array}\,=\text{id}\quad. +\end{align*} +Proofs in the code notation are shorter than in Scala syntax because +certain names and keywords (such as \lstinline!Left!, \lstinline!Right!, +\lstinline!case!, \lstinline!match!, etc.) are omitted. From now +on, we will prefer to use the code notation in proofs, keeping in +mind that one can always convert the code notation to Scala. -Expand the type signature and try implementing this function: -\begin{lstlisting} -def m[A, B, T] : (A => T) => (A => B) => B => T = { r => f => b => ??? } -\end{lstlisting} -Given values $r^{:A\rightarrow T}$, $f^{:A\rightarrow B}$, and $b^{:B}$, -we need to compute a value of type $T$: -\[ -m=r^{:A\rightarrow T}\rightarrow f^{:A\rightarrow B}\rightarrow b^{:B}\rightarrow???^{:T}\quad. -\] -The only way of getting a value of type $T$ is to apply $r$ to some -value of type $A$: +Note that the function arrow ($\rightarrow$) binds weaker than the +pipe operation ($\triangleright$), so the code notation $x\rightarrow y\triangleright z$ +means $x\rightarrow(y\triangleright z)$. We will review the code +notation more systematically in Chapter~\ref{chap:Reasoning-about-code}. + +\subsubsection{Example \label{subsec:ch-Example-type-identity-6}\ref{subsec:ch-Example-type-identity-6}} + +Verify the type equivalence: \[ -m=r^{:A\rightarrow T}\rightarrow f^{:A\rightarrow B}\rightarrow b^{:B}\rightarrow r(???^{:A})\quad. +A\times B\rightarrow C\cong A\rightarrow B\rightarrow C\quad. \] -However, we do not have any values of type $A$. We have a function -$f^{:A\rightarrow B}$ that \emph{consumes} values of type $A$, and -we cannot use $f$ to produce any values of type $A$. So, it seems -that we are unable to fill the typed hole $\text{???}^{:A}$ and implement -the function \lstinline!m!. -In order to verify that \lstinline!m! is unimplementable, we need -to prove that the logical formula: -\begin{equation} -\forall(\alpha,\beta,\tau).\,(\alpha\Rightarrow\tau)\Rightarrow(\alpha\Rightarrow\beta)\Rightarrow(\beta\Rightarrow\tau)\label{eq:ch-example-boolean-formula-3} -\end{equation} -is not true in the constructive logic. We could use the \lstinline!curryhoward! -library for that: + +\subparagraph{Solution} + +Begin by implementing the two functions: \begin{lstlisting} -@ def m[A, B, T] : (A => T) => (A => B) => B => T = implement -cmd1.sc:1: type (A => T) => (A => B) => B => T cannot be implemented -def m[A, B, T] : (A => T) => (A => B) => B => T = implement - ^ -Compilation Failed +def f1[A,B,C]: (((A, B)) => C) => A => B => C = ??? +def f2[A,B,C]: (A => B => C) => ((A, B)) => C = ??? \end{lstlisting} -Another way is to check whether this formula is true in Boolean logic. -A formula that holds in constructive logic will always hold in Boolean -logic, because all rules shown in Section~\ref{subsec:The-rules-of-proof} -preserve Boolean truth values (see Section~\ref{subsec:Relationship-between-Boolean} -for a proof). It follows that any formula that fails to hold in Boolean -logic will also not hold in constructive logic. - -It is relatively easy to check whether a given Boolean formula is -always equal to $True$. Simplifying Eq.~(\ref{eq:ch-example-boolean-formula-3}) -with the rules of Boolean logic, we find: +The Scala code can be derived from the type signatures unambiguously: +\begin{lstlisting} +def f1[A,B,C]: (((A, B)) => C) => A => B => C = g => a => b => g((a, b)) +def f2[A,B,C]: (A => B => C) => ((A, B)) => C = h => { case (a, b) => h(a)(b) } +\end{lstlisting} +Write these functions in the code notation: \begin{align*} - & (\alpha\Rightarrow\tau)\,\gunderline{\Rightarrow}\,(\alpha\Rightarrow\beta)\,\gunderline{\Rightarrow}\,(\beta\Rightarrow\tau)\\ -{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & =\neg(\gunderline{\alpha\Rightarrow\tau})\vee\neg(\gunderline{\alpha\Rightarrow\beta})\vee(\gunderline{\beta\Rightarrow\tau})\\ -{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & =\gunderline{\neg(\neg\alpha\vee\tau)}\vee\gunderline{\neg(\neg\alpha\vee\beta)}\vee(\neg\beta\vee\tau)\\ -{\color{greenunder}\text{use de Morgan\textsf{'}s law}:}\quad & =\left(\alpha\wedge\neg\tau\right)\vee\gunderline{\left(\alpha\wedge\neg\beta\right)\vee\neg\beta}\vee\tau\\ -{\color{greenunder}\text{use identity }(p\wedge q)\vee q=q:}\quad & =\gunderline{\left(\alpha\wedge\neg\tau\right)}\vee\neg\beta\vee\gunderline{\tau}\\ -{\color{greenunder}\text{use identity }(p\wedge\neg q)\vee q=p\vee q:}\quad & =\alpha\vee\neg\beta\vee\tau\quad. + & f_{1}=g^{:A\times B\rightarrow C}\rightarrow a^{:A}\rightarrow b^{:B}\rightarrow g(a\times b)\quad,\\ + & f_{2}=h^{:A\rightarrow B\rightarrow C}\rightarrow\left(a\times b\right)^{:A\times B}\rightarrow h(a)(b)\quad. \end{align*} -This formula is not identically $True$: it is $False$ when $\alpha=\tau=False$ -and $\beta=True$. So, Eq.~(\ref{eq:ch-example-boolean-formula-3}) -is not true in Boolean logic, therefore it is also not true in constructive -logic. By the CH correspondence, we conclude that the type signature -of \lstinline!m! cannot be implemented by a fully parametric function. +We denote by $\left(a\times b\right)^{:A\times B}$ the argument of +type \lstinline!(A, B)! with pattern matching implied. This notation +allows us to write shorter code formulas involving tupled arguments. -\subsubsection{Example \label{subsec:ch-solvedExample-7}\ref{subsec:ch-solvedExample-7}} +Compute the function composition $f_{1}\bef f_{2}$ and show that +it is equal to an identity function: +\begin{align*} +{\color{greenunder}\text{expect to equal }\text{id}:}\quad & f_{1}\bef f_{2}=(g\rightarrow\gunderline{a\rightarrow b\rightarrow g(a\times b)})\bef\left(h\rightarrow a\times b\rightarrow h(a)(b)\right)\\ +{\color{greenunder}\text{composition}:}\quad & \quad=g\rightarrow\gunderline{a\times b\rightarrow g(a\times b)}\\ +{\color{greenunder}\text{simplify function}:}\quad & \quad=\left(g\rightarrow g\right)=\text{id}\quad. +\end{align*} +Compute the function composition $f_{2}\bef f_{1}$ and show that +it is equal to an identity function: +\begin{align*} +{\color{greenunder}\text{expect to equal }\text{id}:}\quad & f_{2}\bef f_{1}=(h\rightarrow\gunderline{a\times b\rightarrow h(a)(b)})\bef\left(g\rightarrow a\rightarrow b\rightarrow g(a\times b)\right)\\ +{\color{greenunder}\text{composition}:}\quad & \quad=h\rightarrow a\rightarrow\gunderline{b\rightarrow h(a)(b)}\\ +{\color{greenunder}\text{simplify }b\rightarrow h(a)(b):}\quad & \quad=h\rightarrow\gunderline{a\rightarrow h(a)}\\ +{\color{greenunder}\text{simplify }a\rightarrow h(a)\text{ to }h:}\quad & \quad=\left(h\rightarrow h\right)=\text{id}\quad. +\end{align*} -Define the type constructor $P^{A}\triangleq\bbnum 1+A+A$ and implement -\lstinline!map! for it: -\[ -\text{map}^{A,B}:P^{A}\rightarrow(A\rightarrow B)\rightarrow P^{B}\quad. -\] -To check that \lstinline!map! preserves information, verify the law -\lstinline!map(p)(x => x) == p! for all \lstinline!p: P[A]!. -\subparagraph{Solution} +\section{Summary} -It is implied that \lstinline!map! should be fully parametric and -information-preserving. Begin by defining a Scala type constructor -for the notation $P^{A}\triangleq\bbnum 1+A+A$: +What tasks can we perform now? +\begin{itemize} +\item Convert a fully parametric type signature into a logical formula and: +\begin{itemize} +\item Decide whether the type signature can be implemented in code. +\item If possible, derive the code using the CH correspondence. +\end{itemize} +\item Use the type notation (Table~\ref{tab:ch-correspondence-type-notation-CH-propositions}) +for reasoning about types to: +\begin{itemize} +\item Decide type equivalence using the rules in Tables~\ref{tab:Logical-identities-with-disjunction-and-conjunction}\textendash \ref{tab:Logical-identities-with-function-types}. +\item Simplify type expressions before writing code. +\end{itemize} +\item Use the matrix notation and the pipe notation to write code that works +on disjunctive types. +\end{itemize} +What tasks \emph{cannot} be performed with these tools? +\begin{itemize} +\item Automatically generate code for \emph{recursive} functions. The CH +correspondence is based on propositional logic, which cannot describe +recursion. Accordingly, recursion is absent from the eight code constructions +of Section~\ref{subsec:The-rules-of-proof}. Recursive functions +need to be coded by hand. +\item Automatically generate code satisfying a property (e.g., isomorphism). +We may generate some code, but the CH correspondence does not guarantee +that properties will hold. We need to verify the required properties +manually, after deriving the code. +\item Express complicated conditions (e.g., \textsf{``}array is sorted\textsf{''}) in a +type signature. This can be done using \textbf{dependent types}\index{dependent type} +(i.e., types that directly depend on values in some way). This is +an advanced technique beyond the scope of this book. Programming languages +such as Coq, Agda, and Idris fully support dependent types, while +Scala has only limited support. +\item Generate code using type constructors with known methods (e.g., the +\lstinline!map! method). +\end{itemize} +As an example of using type constructors with known methods, consider +this type signature: \begin{lstlisting} -sealed trait P[A] -final case class P1[A]() extends P[A] -final case class P2[A](x: A) extends P[A] -final case class P3[A](x: A) extends P[A] +def q[A]: Array[A] => (A => Option[B]) => Array[Option[B]] \end{lstlisting} -Now we can write code to implement the required type signature. Each -time we have several choices of an implementation, we will choose -to preserve information as much as possible. - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0\baselineskip} - +Can we generate the code of this function from its type signature? +We know that the Scala library defines a \lstinline!map! method on +the \lstinline!Array! class. So, an implementation of \lstinline!q! +is: \begin{lstlisting} -def map[A, B]: P[A] => (A => B) => P[B] = - p => f => p match { - case P1() => P1() // No other choice. - case P2(x) => ??? - case P3(x) => ??? - } +def q[A]: Array[A] => (A => Option[B]) => Array[Option[B]] = { arr => f => arr.map(f) } \end{lstlisting} -\vspace{-0.9\baselineskip} -\end{wrapfigure}% +However, it is hard to create an \emph{algorithm} that can derive +this implementation automatically from the type signature of \lstinline!q! +via the Curry-Howard correspondence. The algorithm would have to convert +the type signature of \lstinline!q! into this logical formula: +\begin{equation} +{\cal CH}(\text{Array}^{A})\Rightarrow{\cal CH}(A\rightarrow\text{Opt}^{B})\Rightarrow{\cal CH}(\text{Array}^{\text{Opt}^{B}})\quad.\label{eq:ch-example-quantified-proposition} +\end{equation} +To derive an implementation, the algorithm would need to use the available +\lstinline!map! method for \lstinline!Array!. That method has the +type signature: +\[ +\text{map}:\forall(A,B).\,\text{Array}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Array}^{B}\quad. +\] +To derive the ${\cal CH}$-proposition~(\ref{eq:ch-example-quantified-proposition}), +the algorithm will need to assume that the ${\cal CH}$-proposition: +\begin{equation} +{\cal CH}\,\big(\forall(A,B).\,\text{Array}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Array}^{B}\big)\label{eq:ch-example-quantified-proposition-2} +\end{equation} +already holds. In other words Eq.~(\ref{eq:ch-example-quantified-proposition-2}) +is one of the premises of a sequent. Reasoning about premises such +as Eq.~(\ref{eq:ch-example-quantified-proposition-2}) requires \index{first-order logic}\textbf{first-order +logic} \textemdash{} a logic whose proof rules can handle quantified +types such as $\forall(A,B)$\emph{ }inside premises. However, first-order +logic is \textbf{undecidable}\index{undecidable logic}: no algorithm +can find a proof (or verify the absence of a proof) in all cases. -\noindent In the case \lstinline!P2(x)!, we are required to produce -a value of type $P^{B}$ from a value $x^{:A}$ and a function $f^{:A\rightarrow B}$. -Since $P^{B}$ is a disjunctive type with three parts, we can produce -a value of type $P^{B}$ in three different ways: \lstinline!P1()!, -\lstinline!P2(...)!, and \lstinline!P3(...)!. If we return \lstinline!P1()!, -we will lose the information about the value \lstinline!x!. If we -return \lstinline!P3(...)!, we will preserve the information about -\lstinline!x! but lose the information\index{information loss} that -the input value was a \lstinline!P2! rather than a \lstinline!P3!. -By returning \lstinline!P2(...)! in that scope, we preserve the entire -input information. +The constructive propositional logic with the rules listed in Table~\ref{tab:Proof-rules-for-constructive-logic} +is \textbf{decidable},\index{decidable logic} i.e., it has an algorithm +that either finds a proof or disproves any given formula. However, +that logic cannot handle type constructors such as $\text{Array}^{A}$. +It also cannot handle premises containing type quantifiers such as +$\forall(A,B)$, because all the available rules have the quantifiers +placed \emph{outside} the premises. -The value under \lstinline!P2(...)! must be of type $B$, and the -only way of getting a value of type $B$ is to apply $f$ to $x$. -So, we return \lstinline!P2(f(x))!. +So, code for functions such as \lstinline!q! can only be derived +by trial and error, informed by intuition. This book will help programmers +to acquire the necessary intuition and technique. -Similarly, in the case \lstinline!P3(x)!, we should return \lstinline!P3(f(x))!. -The final code of \lstinline!map! is: -\begin{lstlisting} -def map[A, B]: P[A] => (A => B) => P[B] = p => f => p match { - case P1() => P1() // No other choice here. - case P2(x) => P2(f(x)) // Preserve information. - case P3(x) => P3(f(x)) // Preserve information. -} -\end{lstlisting} +\subsection{Examples\index{examples (with code)}} -To verify the given law, we first write a matrix notation for \lstinline!map!: +\subsubsection{Example \label{subsec:ch-solvedExample-1}\ref{subsec:ch-solvedExample-1}} + +Find the cardinality of the type \lstinline!P = Option[Option[Boolean] => Boolean]!. +Write \lstinline!P! in the type notation and simplify it to an equivalent +type. + +\subparagraph{Solution} + +Begin with the type \lstinline!Option[Boolean]!, which can be either +\lstinline!None! or \lstinline!Some(x)! with some value \lstinline!x: Boolean!. +Because the type \lstinline!Boolean! has $2$ possible values, the +type \lstinline!Option[Boolean]! has $3$ possible values: \[ -\text{map}^{A,B}\triangleq p^{:\bbnum 1+A+A}\rightarrow f^{:A\rightarrow B}\rightarrow p\triangleright\,\begin{array}{|c||ccc|} - & \bbnum 1 & B & B\\ -\hline \bbnum 1 & \text{id} & \bbnum 0 & \bbnum 0\\ -A & \bbnum 0 & f & \bbnum 0\\ -A & \bbnum 0 & \bbnum 0 & f -\end{array}\quad. +|\text{Opt}^{\text{Boolean}}|=\left|\bbnum 1+\text{Boolean}\right|=1+\left|\text{Boolean}\right|=1+2=3\quad. +\] +In the type notation, \lstinline!Boolean! is denoted by the symbol +$\bbnum 2$ and \lstinline!Option[Boolean]! by $\bbnum 1+\bbnum 2$. +So, the type notation $\bbnum 1+\bbnum 2$ is consistent with the +cardinality $3$ of that type: +\[ +\left|\bbnum 1+\text{Boolean}\right|=\left|\bbnum 1+\bbnum 2\right|=1+2=3\quad. \] -The required law is written as an equation $\text{map}\left(p\right)(\text{id})=p$, -called the\index{identity laws!of map@of \texttt{map}} \textbf{identity -law}. Substituting the code notation for \lstinline!map!, we verify -the law: -\begin{align*} -{\color{greenunder}\text{expect to equal }p:}\quad & \text{map}\left(p\right)(\text{id})\\ -{\color{greenunder}\text{apply \texttt{map()()} to arguments}:}\quad & =p\triangleright\,\begin{array}{||ccc|} -\text{id} & \bbnum 0 & \bbnum 0\\ -\bbnum 0 & \text{id} & \bbnum 0\\ -\bbnum 0 & \bbnum 0 & \text{id} -\end{array}\\ -{\color{greenunder}\text{identity function in matrix notation}:}\quad & =p\triangleright\text{id}\\ -{\color{greenunder}\triangleright\text{-notation}:}\quad & =\text{id}\left(p\right)=p\quad. -\end{align*} - -\subsubsection{Example \label{subsec:ch-solvedExample-8}\ref{subsec:ch-solvedExample-8}} +The function type \lstinline!Option[Boolean] => Boolean! is denoted +by $\bbnum 1+\bbnum 2\rightarrow\bbnum 2$. Compute its cardinality +as: +\[ +|\text{Opt}^{\text{Boolean}}\rightarrow\text{Boolean}|=\left|\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right|=\left|\bbnum 2\right|^{\left|\bbnum 1+\bbnum 2\right|}=2^{3}=8\quad. +\] +Finally, the we write \lstinline!P! in the type notation as $P=\bbnum 1+\left(\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right)$ +and find: +\[ +\left|P\right|=\left|\bbnum 1+\left(\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right)\right|=1+\left|\bbnum 1+\bbnum 2\rightarrow\bbnum 2\right|=1+8=9\quad. +\] -Implement \lstinline!map! and \lstinline!flatMap! for \lstinline!Either[L, R]!, -applied to the type parameter \lstinline!L!. -\subparagraph{Solution} +\subsubsection{Example \label{subsec:ch-solvedExample-2}\ref{subsec:ch-solvedExample-2}} -For a type constructor, say, $P^{A}$, the standard type signatures -for \lstinline!map! and \lstinline!flatMap! are: +Implement a Scala type \lstinline!P[A]! given by this type notation: \[ -\text{map}:P^{A}\rightarrow(A\rightarrow B)\rightarrow P^{B}\quad,\quad\quad\text{flatMap}:P^{A}\rightarrow(A\rightarrow P^{B})\rightarrow P^{B}\quad. +P^{A}\triangleq1+A+\text{Int}\times A+(\text{String}\rightarrow A)\quad. \] -If a type constructor has more than one type parameter, e.g., $P^{A,S,T}$, -one can define the functions \lstinline!map! and \lstinline!flatMap! -applied to a chosen type parameter. For example, when applied to the -type parameter $A$, the type signatures are: -\begin{align*} -\text{map} & :P^{A,S,T}\rightarrow(A\rightarrow B)\rightarrow P^{B,S,T}\quad,\\ -\text{flatMap} & :P^{A,S,T}\rightarrow(A\rightarrow P^{B,S,T})\rightarrow P^{B,S,T}\quad. -\end{align*} -Being \textsf{``}applied to the type parameter $A$\textsf{''} means that the other -type parameters $S,T$ in $P^{A,S,T}$ remain fixed while the type -parameter $A$ is replaced by $B$ in the type signatures of \lstinline!map! -and \lstinline!flatMap!. -For the type \lstinline!Either[L, R]! (in the type notation, $L+R$), -we keep the type parameter $R$ fixed while $L$ is replaced by $M$. -So we obtain the type signatures: -\begin{align*} -\text{map} & :L+R\rightarrow(L\rightarrow M)\rightarrow M+R\quad,\\ -\text{flatMap} & :L+R\rightarrow(L\rightarrow M+R)\rightarrow M+R\quad. -\end{align*} -Implementing these functions is straightforward: -\begin{lstlisting} -def map[L,M,R]: Either[L, R] => (L => M) => Either[M, R] = e => f => e match { - case Left(x) => Left(f(x)) - case Right(y) => Right(y) -} - -def flatMap[L,M,R]: Either[L, R] => (L => Either[M, R]) => Either[M, R] = e => f => e match { - case Left(x) => f(x) - case Right(y) => Right(y) -} -\end{lstlisting} -The code notation for these functions is: -\begin{align*} -\text{map} & \triangleq e^{:L+R}\rightarrow f^{:L\rightarrow M}\rightarrow e\triangleright\,\begin{array}{|c||cc|} - & M & R\\ -\hline L & f & \bbnum 0\\ -R & \bbnum 0 & \text{id} -\end{array}\quad,\\ -\text{flatMap} & \triangleq e^{:L+R}\rightarrow f^{:L\rightarrow M+R}\rightarrow e\triangleright\,\begin{array}{|c||c|} - & M+R\\ -\hline L & f\\ -R & y^{:R}\rightarrow\bbnum 0^{:M}+y -\end{array}\quad. -\end{align*} -Note that the code matrix for \lstinline!flatMap! cannot be split -into the $M$ and $R$ columns because we do not know in advance which -part of the disjunctive type $M+R$ will be returned when we evaluate -$f(x^{:L})$. - -\subsubsection{Example \label{subsec:ch-solvedExample-9}\ref{subsec:ch-solvedExample-9}{*}} - -Define a type constructor $\text{State}^{S,A}\equiv S\rightarrow A\times S$ -and implement the functions: - -\textbf{(a)} $\text{pure}^{S,A}:A\rightarrow\text{State}^{S,A}\quad.$ - -\textbf{(b)} $\text{map}^{S,A,B}:\text{State}^{S,A}\rightarrow(A\rightarrow B)\rightarrow\text{State}^{S,B}\quad.$ - -\textbf{(c)} $\text{flatMap}^{S,A,B}:\text{State}^{S,A}\rightarrow(A\rightarrow\text{State}^{S,B})\rightarrow\text{State}^{S,B}\quad.$ \subparagraph{Solution} -It is assumed that all functions must be fully parametric and preserve -as much information as possible. We define the type alias: +To translate type notation into Scala code, begin by defining the +disjunctive types as case classes, choosing class names for convenience. +In this case, $P^{A}$ is a disjunctive type with four parts, so we +need four case classes: \begin{lstlisting} -type State[S, A] = S => (A, S) +sealed trait P[A] +final case class P1[A](???) extends P[A] +final case class P2[A](???) extends P[A] +final case class P3[A](???) extends P[A] +final case class P4[A](???) extends P[A] \end{lstlisting} - -\textbf{(a)} The type signature is $A\rightarrow S\rightarrow A\times S$, -and there is only one implementation: +Each of the case classes represents one part of the disjunctive type. +Now we write the contents for each of the case classes, in order to +implement the data in each of the disjunctive parts: \begin{lstlisting} -def pure[S, A]: A => State[S, A] = a => s => (a, s) +sealed trait P[A] +final case class P1[A]() extends P[A] +final case class P2[A](x: A) extends P[A] +final case class P3[A](n: Int, x: A) extends P[A] +final case class P4[A](f: String => A) extends P[A] \end{lstlisting} -In the code notation, this is written as: -\[ -\text{pu}^{S,A}\triangleq a^{:A}\rightarrow s^{:S}\rightarrow a\times s\quad. -\] -\textbf{(b)} The type signature is: -\[ -\text{map}^{S,A,B}:(S\rightarrow A\times S)\rightarrow(A\rightarrow B)\rightarrow S\rightarrow B\times S\quad. -\] -Begin writing a Scala implementation: -\begin{lstlisting} -def map[S, A, B]: State[S, A] => (A => B) => State[S, B] = { t => f => s => ??? } -\end{lstlisting} -We need to compute a value of $B\times S$ from the curried arguments -$t^{:S\rightarrow A\times S}$, $f^{:A\rightarrow B}$, and $s^{:S}$. -We begin writing the code of \lstinline!map! using a typed hole: -\[ -\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow\text{???}^{:B}\times\text{???}^{:S}\quad. -\] -The only way of getting a value of type $B$ is by applying $f$ to -a value of type $A$: -\[ -\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow f(\text{???}^{:A})\times\text{???}^{:S}\quad. -\] -The only possibility of filling the typed hole $\text{???}^{:A}$ -is to apply $t$ to a value of type $S$. We already have such a value, -$s^{:S}$. Computing $t(s)$ yields a pair of type $A\times S$, from -which we may take the first part (of type $A$) to fill the typed -hole $\text{???}^{:A}$. The second part of the pair is a value of -type $S$ that we may use to fill the second typed hole, $\text{???}^{:S}$. -So, the Scala code is: -\begin{wrapfigure}{l}{0.62\columnwidth}% -\vspace{-0.9\baselineskip} -\begin{lstlisting}[numbers=left] -def map[S, A, B]: State[S, A] => (A => B) => State[S, B] = { - t => f => s => - val (a, s2) = t(s) - (f(a), s2) // We could also return `(f(a), s)` here. -} -\end{lstlisting} -\vspace{-1.25\baselineskip} -\end{wrapfigure}% +\subsubsection{Example \label{subsec:ch-solvedExample-2a}\ref{subsec:ch-solvedExample-2a}} -\noindent Why not return the original value \lstinline!s! in the -tuple $B\times S$, instead of the new value \lstinline!s2!? The -reason is that we would like to preserve information as much as possible. -If we return \lstinline!(f(a), s)! in line 4, we will have discarded -the computed value \lstinline!s2!, which is a loss of information. +Find an equivalent disjunctive type for the type \lstinline!P = (Either[A, B], Either[C, D])!. -To write the code notation for \lstinline!map!, we need to destructure -the pair that $t(s)$ returns. We can write explicit destructuring -code like this: -\[ -\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow(a^{:A}\times s_{2}^{:S}\rightarrow f(a)\times s_{2})(t(s))\quad. -\] -If we temporarily denote by $q$ the following destructuring function: -\[ -q\triangleq(a^{:A}\times s_{2}^{:S}\rightarrow f(a)\times s_{2})\quad, -\] -we will notice that the expression $s\rightarrow q(t(s))$ is a function -composition applied to $s$. So, we rewrite $s\rightarrow q(t(s))$ -as the composition $t\bef q$ and obtain shorter code: -\[ -\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow t\bef(a^{:A}\times s^{:S}\rightarrow f(a)\times s)\quad. -\] -Shorter formulas are often easier to reason about in derivations, -although not necessarily easier to read when converted to program -code. +\subparagraph{Solution} -\textbf{(c)} The required type signature is: +Begin by writing the given type in the type notation. The tuple becomes +the product type, and \lstinline!Either! becomes the disjunctive +(or \textsf{``}sum\textsf{''}) type: \[ -\text{flatMap}^{S,A,B}:(S\rightarrow A\times S)\rightarrow(A\rightarrow S\rightarrow B\times S)\rightarrow S\rightarrow B\times S\quad. +P\triangleq(A+B)\times(C+D)\quad. \] -We perform code reasoning with typed holes: +By the usual rules of arithmetic, we expand brackets and obtain an +equivalent type: \[ -\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow\text{???}^{:B\times S}\quad. +P\cong A\times C+A\times D+B\times C+B\times D\quad. \] -To fill $\text{???}^{:B\times S}$, we need to apply $f$ to some -arguments, since $f$ is the only function that returns any values -of type $B$. Applying $f$ to two values will yield a value of type -$B\times S$, just as we need: +This is a disjunctive type having $4$ parts. + +\subsubsection{Example \label{subsec:ch-solvedExample-3}\ref{subsec:ch-solvedExample-3}} + +Show that the following type equivalences do \emph{not} hold: $A+A\not\cong A$ +and $A\times A\not\cong A$, although the corresponding logical identities +hold. + +\subparagraph{Solution} + +Note that the arithmetic equalities do not hold, $A+A\neq A$ and +$A\times A\ne A$. This already indicates that the types are not equivalent. +To build further intuition, consider that a value of type $A+A$ (in +Scala, \lstinline!Either[A, A]!) is a \lstinline!Left(a)! or a \lstinline!Right(a)! +for some \lstinline!a:A!. In the code notation, it is either $a^{:A}+\bbnum 0$ +or $\bbnum 0+a^{:A}$. So, a value of type $A+A$ contains a value +of type $A$ with the additional information about whether it is the +first or the second part of the disjunctive type. We cannot represent +that information in a single value of type $A$. + +Similarly, a value of type $A\times A$ contains two (possibly different) +values of type $A$, which cannot be represented by a single value +of type $A$ without loss of information. + +However, the corresponding logical identities $\alpha\vee\alpha=\alpha$ +and $\alpha\wedge\alpha=\alpha$ hold. To see that, we could derive +the four formulas: \[ -\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow f(\text{???}^{:A})(\text{???}^{:S})\quad. +\alpha\vee\alpha\Rightarrow\alpha\quad,\quad\quad\alpha\Rightarrow\alpha\vee\alpha\quad,\quad\quad\alpha\wedge\alpha\Rightarrow\alpha\quad,\quad\quad\alpha\Rightarrow\alpha\wedge\alpha\quad, \] -To fill the new typed holes, we need to apply $t$ to an argument -of type $S$. We have only one given value $s^{:S}$ of type $S$, -so we must compute $t(s)$ and destructure it: +using the proof rules of Section~\ref{subsec:The-rules-of-proof}. +Alternatively, we may use the CH correspondence and show that the +type signatures: \[ -\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow\left(a\times s_{2}\rightarrow f(a)(s_{2})\right)(t(s))\quad. +\forall A.\,A+A\rightarrow A\quad,\quad\quad\forall A.\,A\rightarrow A+A\quad,\quad\quad\forall A.\,A\times A\rightarrow A\quad,\quad\quad\forall A.\,A\rightarrow A\times A\quad \] -Translating this notation into Scala code, we obtain: +can be implemented via fully parametric functions. For a programmer, +it is easier to write code than to guess the correct sequence of proof +rules. For the first pair of type signatures, we find: \begin{lstlisting} -def flatMap[S, A, B]: State[S, A] => (A => State[S, B]) => State[S, B] = { - t => f => s => - val (a, s2) = t(s) - f(a)(s2) // We could also return `f(a)(s)` here, but that would lose information. +def f1[A]: Either[A, A] => A = { + case Left(a) => a // No other choice here. + case Right(a) => a // No other choice here. } +def f2[A]: A => Either[A, A] = { a => Left(a) } // Can be also Right(a). \end{lstlisting} -In order to preserve information, we choose not to discard the computed -value \lstinline!s2!. +The presence of an arbitrary choice, to return \lstinline!Left(a)! +or \lstinline!Right(a)!, is a warning sign showing that additional +information is required to create a value of type \lstinline!Either[A, A]!. +This is precisely the information present in the type $A+A$ but missing +in the type $A$. -The code notation for this \lstinline!flatMap! can be simplified -to: +The code notation for these functions is: +\begin{align*} +f_{1} & \triangleq\,\begin{array}{|c||c|} + & A\\ +\hline A & a^{:A}\rightarrow a\\ +A & a^{:A}\rightarrow a +\end{array}\,=\,\begin{array}{|c||c|} + & A\\ +\hline A & \text{id}\\ +A & \text{id} +\end{array}\quad,\\ +f_{2} & \triangleq a^{:A}\rightarrow a+\bbnum 0^{:A}=\,\begin{array}{|c||cc|} + & A & A\\ +\hline A & a^{:A}\rightarrow a & \bbnum 0 +\end{array}\,=\,\begin{array}{|c||cc|} + & A & A\\ +\hline A & \text{id} & \bbnum 0 +\end{array}\quad. +\end{align*} +The composition of these functions is \emph{not} equal to identity: \[ -\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow t\bef\left(a\times s\rightarrow f(a)(s)\right)\quad. +f_{1}\bef f_{2}=\,\begin{array}{||c|} +\text{id}\\ +\text{id} +\end{array}\,\bef\,\begin{array}{||cc|} +\text{id} & \bbnum 0\end{array}\,=\,\begin{array}{||cc|} +\text{id} & \bbnum 0\\ +\text{id} & \bbnum 0 +\end{array}\,\quad,\quad\text{while we have}\quad\text{id}^{:A+A\rightarrow A+A}=\,\begin{array}{||cc|} +\text{id} & \bbnum 0\\ +\bbnum 0 & \text{id} +\end{array}\quad. \] +For the second pair of type signatures, the code is: +\begin{lstlisting} +def f1[A]: ((A, A)) => A = { case (a1, a2) => a1 } // Could be also `a2`. +cef f2[A]: A => (A, A) = { a => (a, a) } // No other choice here. +\end{lstlisting} +It is clear that the first function loses information when it returns +\lstinline!a1! and discards \lstinline!a2! (or vice versa). -\subsection{Exercises\index{exercises}} - -\subsubsection{Exercise \label{subsec:ch-Exercise-0}\ref{subsec:ch-Exercise-0}} - -Find the cardinality of the type \lstinline!P = Option[Boolean => Option[Boolean]]!. -Show that \lstinline!P! is equivalent to \lstinline!Option[Boolean] => Boolean!, -and that the equivalence is accidental\index{type equivalence!accidental} -and not \textsf{``}natural\textsf{''}. - -\subsubsection{Exercise \label{subsec:ch-Exercise-1-a}\ref{subsec:ch-Exercise-1-a}} - -Verify the type equivalences $A+A\cong\bbnum 2\times A$ and $A\times A\cong\bbnum 2\rightarrow A$, -where $\bbnum 2$ denotes the \lstinline!Boolean! type. +The code notation for the functions \lstinline!f1! and \lstinline!f2! +is: +\[ +f_{1}\triangleq a_{1}^{:A}\times a_{2}^{:A}\rightarrow a_{1}=\pi_{1}^{:A\times A\rightarrow A}\quad,\quad\quad f_{2}\triangleq a^{:A}\rightarrow a\times a=\Delta^{:A\rightarrow A\times A}\quad. +\] +Computing the compositions of these functions, we find that $f_{2}\bef f_{1}=\text{id}$ +while $f_{1}\bef f_{2}\ne\text{id}$: +\begin{align*} +f_{1}\bef f_{2} & =\left(a_{1}\times a_{2}\rightarrow a_{1}\right)\bef\left(a\rightarrow a\times a\right)\\ + & =\left(a_{1}\times a_{2}\rightarrow a_{1}\times a_{1}\right)\neq\text{id}=\left(a_{1}\times a_{2}\rightarrow a_{1}\times a_{2}\right)\quad. +\end{align*} -\subsubsection{Exercise \label{subsec:ch-Exercise-1}\ref{subsec:ch-Exercise-1}} +We have implemented all four type signatures as fully parametric functions, +which shows that the corresponding logical formulas are all true (i.e., +can be derived using the proof rules). However, the functions cannot +be inverses of each other. So, the type equivalences do not hold. -Show that $A\Rightarrow(B\vee C)\neq(A\Rightarrow B)\wedge(A\Rightarrow C)$ -in constructive and Boolean logic. +\subsubsection{Example \label{subsec:ch-solvedExample-4}\ref{subsec:ch-solvedExample-4}} -\subsubsection{Exercise \label{subsec:ch-solvedExample-5-1}\ref{subsec:ch-solvedExample-5-1}} +Show that $\left(\left(A\wedge B\right)\Rightarrow C\right)\neq(A\Rightarrow C)\vee(B\Rightarrow C)$ +in the constructive logic, but the equality holds in Boolean logic. +This is another example where the Boolean reasoning fails to give +correct answers about implementability of type signatures. -Verify the type equivalence $\left(A\rightarrow B\times C\right)\cong\left(A\rightarrow B\right)\times\left(A\rightarrow C\right)$ -with full proofs. +\subparagraph{Solution} -\subsubsection{Exercise \label{subsec:ch-Exercise-type-identity-4}\ref{subsec:ch-Exercise-type-identity-4}} - -Use known rules to verify the type equivalences without need for proofs: - -\textbf{(a)} $\left(A+B\right)\times\left(A\rightarrow B\right)\cong A\times\left(A\rightarrow B\right)+\left(\bbnum 1+A\rightarrow B\right)\quad.$ - -\textbf{(b)} $\left(A\times(\bbnum 1+A)\rightarrow B\right)\cong\left(A\rightarrow B\right)\times\left(A\rightarrow A\rightarrow B\right)\quad.$ - -\textbf{(c)} $A\rightarrow\left(\bbnum 1+B\right)\rightarrow C\times D\cong\left(A\rightarrow C\right)\times\left(A\rightarrow D\right)\times\left(A\times B\rightarrow C\right)\times\left(A\times B\rightarrow D\right)\quad.$ - -\subsubsection{Exercise \label{subsec:ch-Exercise-2}\ref{subsec:ch-Exercise-2}} - -Write the type notation for \lstinline!Either[(A, Int), Either[(A, Char), (A, Float)]]!. -Transform this type into an equivalent type of the form $A\times(...)$. - -\subsubsection{Exercise \label{subsec:ch-Exercise-3}\ref{subsec:ch-Exercise-3}} - -Define a type $\text{OptE}^{T,A}\triangleq\bbnum 1+T+A$ and implement -information-preserving \lstinline!map! and \lstinline!flatMap! for -it, applied to the type parameter $A$. Get the same result using -the equivalent type $(\bbnum 1+A)+T$, i.e., \lstinline!Either[Option[A], T]!. -The required type signatures are: +Begin by rewriting the logical equality as two implications: \begin{align*} -\text{map}^{A,B,T} & :\text{OptE}^{T,A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{OptE}^{T,B}\quad,\\ -\text{flatMap}^{A,B,T} & :\text{OptE}^{T,A}\rightarrow(A\rightarrow\text{OptE}^{T,B})\rightarrow\text{OptE}^{T,B}\quad. +(A\wedge B & \Rightarrow C)\Rightarrow(A\Rightarrow C)\vee(B\Rightarrow C)\\ +\quad\text{ and }\quad & \left((A\Rightarrow C)\vee(B\Rightarrow C)\right)\Rightarrow\left(\left(A\wedge B\right)\Rightarrow C\right)\quad. \end{align*} - - -\subsubsection{Exercise \label{subsec:ch-Exercise-4}\ref{subsec:ch-Exercise-4}} - -Implement the \lstinline!map! function for the type constructor \lstinline!P[A]! -from Example~\ref{subsec:ch-solvedExample-2}. The required type -signature is $P^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow P^{B}$. -Preserve information as much as possible. - -\subsubsection{Exercise \label{subsec:ch-Exercise-4-1}\ref{subsec:ch-Exercise-4-1}} - -For the type constructor $Q^{T,A}$ defined in Exercise~\ref{subsec:Exercise-type-notation-1}, -define the \lstinline!map! function, preserving information as much -as possible: +It is sufficient to show that one of these implications is incorrect. +Rather than looking for a proof tree in the constructive logic (which +would be difficult, since we need to demonstrate that \emph{no} proof +exists), let us use the CH correspondence. According to the CH correspondence, +an equivalent task is to implement fully parametric functions with +the type signatures: \[ -\text{map}^{T,A,B}:Q^{T,A}\rightarrow\left(A\rightarrow B\right)\rightarrow Q^{T,B}\quad. +(A\times B\rightarrow C)\rightarrow(A\rightarrow C)+(B\rightarrow C)\quad\text{ and }\quad(A\rightarrow C)+(B\rightarrow C)\rightarrow A\times B\rightarrow C\quad. \] +For the first type signature, the Scala code is: +\begin{lstlisting} +def f1[A,B,C]: (((A, B)) => C) => Either[A => C, B => C] = { k => ??? } +\end{lstlisting} +We are required to return either a \lstinline!Left(g)! with \lstinline!g: A => C!, +or a \lstinline!Right(h)! with \lstinline!h: B => C!. The only given +data is a function \lstinline!k! of type $A\times B\rightarrow C$, +so the decision of whether to return a \lstinline!Left! or a \lstinline!Right! +must be hard-coded in the function \lstinline!f1! independently of +\lstinline!k!. Can we produce a function \lstinline!g! of type \lstinline!A => C!? +Given a value of type \lstinline!A!, we would need to return a value +of type \lstinline!C!. The only way to obtain a value of type \lstinline!C! +is by applying \lstinline!k! to some arguments. But to apply \lstinline!k!, +we need a value of type \lstinline!B!, which we do not have. So we +cannot produce a \lstinline!g: A => C!. Similarly, we cannot produce +a function \lstinline!h! of type \lstinline!B => C!. +We repeat the same argument in the type notation. Obtaining a value +of type $(A\rightarrow C)+(B\rightarrow C)$ means to compute either +$g^{:A\rightarrow C}+\bbnum 0$ or $\bbnum 0+h^{:B\rightarrow C}$. +This decision must be hard-coded since the only data is a function +$k^{:A\times B\rightarrow C}$. We can compute $g^{:A\rightarrow C}$ +only by partially applying $k^{:A\times B\rightarrow C}$ to a value +of type $B$. However, we have no values of type $B$. Similarly, +we cannot compute a value $h^{:B\rightarrow C}$. -\subsubsection{Exercise \label{subsec:Exercise-disjunctive-6}\ref{subsec:Exercise-disjunctive-6}} - -Define a recursive type constructor $\text{Tr}_{3}$ as $\text{Tr}_{3}{}^{A}\triangleq\bbnum 1+A\times A\times A\times\text{Tr}_{3}{}^{A}$ -and implement the \lstinline!map! function for it, with the standard -type signature: $\text{map}^{A,B}:\text{Tr}_{3}{}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Tr}_{3}{}^{B}\quad.$ - -\subsubsection{Exercise \label{subsec:ch-Exercise-5}\ref{subsec:ch-Exercise-5}} - -Implement fully parametric, information-preserving functions with -the types: - -\textbf{(a)} $Z+A\times A\rightarrow(A\rightarrow B)\rightarrow Z+B\times B\quad.$ +The inverse type signature \emph{can} be implemented: +\begin{lstlisting} +def f2[A,B,C]: Either[A=>C, B=>C] => ((A,B)) => C = { + case Left(g) => { case (a, b) => g(a) } + case Right(h) => { case (a, b) => h(b) } +} +\end{lstlisting} +\[ +f_{2}\triangleq\,\begin{array}{|c||c|} + & A\times B\rightarrow C\\ +\hline A\rightarrow C & g^{:A\rightarrow C}\rightarrow a\times b\rightarrow g(a)\\ +B\rightarrow C & h^{:B\rightarrow C}\rightarrow a\times b\rightarrow h(b) +\end{array}\quad. +\] -\textbf{(b)} $A+Z\rightarrow B+Z\rightarrow(A\rightarrow B\rightarrow C)\rightarrow C+Z\quad.$ +Let us now show that the logical identity: +\begin{equation} +((\alpha\wedge\beta)\Rightarrow\gamma)=((\alpha\Rightarrow\gamma)\vee(\beta\Rightarrow\gamma))\label{eq:ch-example-identity-boolean-not-constructive} +\end{equation} +holds in Boolean logic. A straightforward calculation is to simplify +the Boolean expression using Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic}), +which only holds in Boolean logic (but not in the constructive logic). +We find: +\begin{align*} +{\color{greenunder}\text{left-hand side of Eq.~(\ref{eq:ch-example-identity-boolean-not-constructive})}:}\quad & \left(\alpha\wedge\beta\right)\gunderline{\Rightarrow}\,\gamma\\ +{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & \quad=\gunderline{\neg(\alpha\wedge\beta)}\vee\gamma\\ +{\color{greenunder}\text{use de Morgan\textsf{'}s law}:}\quad & \quad=\neg\alpha\vee\neg\beta\vee\gamma\quad.\\ +{\color{greenunder}\text{right-hand side of Eq.~(\ref{eq:ch-example-identity-boolean-not-constructive})}:}\quad & (\gunderline{\alpha\Rightarrow\gamma})\vee(\gunderline{\beta\Rightarrow\gamma})\\ +{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & \quad=\neg\alpha\vee\gunderline{\gamma}\vee\neg\beta\vee\gunderline{\gamma}\\ +{\color{greenunder}\text{use identity }\gamma\vee\gamma=\gamma:}\quad & \quad=\neg\alpha\vee\neg\beta\vee\gamma\quad. +\end{align*} +Both sides of Eq.~(\ref{eq:ch-example-identity-boolean-not-constructive}) +are equal to the same formula, $\neg\alpha\vee\neg\beta\vee\gamma$, +so the identity holds. -\textbf{(c)} $P+A\times A\rightarrow(A\rightarrow B)\rightarrow(P\rightarrow A+Q)\rightarrow Q+B\times B\quad.$ +This calculation does not work in the constructive logic because its +proof rules can derive neither the Boolean formula~(\ref{eq:ch-definition-of-implication-in-Boolean-logic}) +nor the \textbf{law of de Morgan}\index{law of de Morgan}, $\neg(\alpha\wedge\beta)=\left(\neg\alpha\vee\neg\beta\right)$. -\textbf{(d)} $\text{flatMap}^{E,A,B}:\text{Read}^{E,A}\rightarrow(A\rightarrow\text{Read}^{E,B})\rightarrow\text{Read}^{E,B}\quad.$ +Another way of proving the Boolean identity~(\ref{eq:ch-example-identity-boolean-not-constructive}) +is to enumerate all possible truth values for the variables $\alpha$, +$\beta$, and $\gamma$. The left-hand side, $\left(\alpha\wedge\beta\right)\Rightarrow\gamma$, +can be $False$ only if $\alpha\wedge\beta=True$ (that is, both $\alpha$ +and $\beta$ are $True$) and $\gamma=False$. For all other truth +values of $\alpha$, $\beta$, and $\gamma$, the formula $\left(\alpha\wedge\beta\right)\Rightarrow\gamma$ +is $True$. Let us determine when the right-hand side, $(\alpha\Rightarrow\gamma)\vee(\beta\Rightarrow\gamma)$, +can be $False$. This can happen only if both parts of the disjunction +are $False$. That means $\alpha=True$, $\beta=True$, and $\gamma=False$. +So, the two sides of the identity~(\ref{eq:ch-example-identity-boolean-not-constructive}) +are both $True$ or both $False$ with any choice of truth values +of $\alpha$, $\beta$, and $\gamma$. In Boolean logic, this is sufficient +to prove the identity~(\ref{eq:ch-example-identity-boolean-not-constructive}). +$\square$ -\textbf{(e)} $\text{State}^{S,A}\rightarrow\left(S\times A\rightarrow B\right)\rightarrow\text{State}^{S,B}\quad.$ +The following example shows how to use the formulas from Tables~\ref{tab:Logical-identities-with-disjunction-and-conjunction}\textendash \ref{tab:Logical-identities-with-function-types} +to derive the type equivalence of complicated type expressions without +need for proofs. -\subsubsection{Exercise \label{subsec:ch-Exercise-7}\ref{subsec:ch-Exercise-7}{*}} +\subsubsection{Example \label{subsec:ch-solvedExample-5-2}\ref{subsec:ch-solvedExample-5-2}} -Denote $\text{Cont}^{R,T}\triangleq\left(T\rightarrow R\right)\rightarrow R$ -and implement the functions: +Use known formulas to verify the type equivalences without direct +proofs: -\textbf{(a)} $\text{map}^{R,T,U}:\text{Cont}^{R,T}\rightarrow(T\rightarrow U)\rightarrow\text{Cont}^{R,U}\quad.$ +\textbf{(a)} $A\times\left(A+\bbnum 1\right)\times\left(A+\bbnum 1+\bbnum 1\right)\cong A\times\left(\bbnum 1+\bbnum 1+A\times\left(\bbnum 1+\bbnum 1+\bbnum 1+A\right)\right)$. -\textbf{(b)} $\text{flatMap}^{R,T,U}:\text{Cont}^{R,T}\rightarrow(T\rightarrow\text{Cont}^{R,U})\rightarrow\text{Cont}^{R,U}\quad.$ +\textbf{(b)} $\bbnum 1+A+B\rightarrow\bbnum 1\times B\cong\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B$. -\subsubsection{Exercise \label{subsec:ch-Exercise-8}\ref{subsec:ch-Exercise-8}{*}} +\subparagraph{Solution} -Denote $\text{Sel}^{Z,T}\triangleq\left(T\rightarrow Z\right)\rightarrow T$ -and implement the functions: +\textbf{(a)} We can expand brackets in type expressions as in arithmetic: +\begin{align*} +A\times\left(A+\bbnum 1\right) & \cong A\times A+A\times\bbnum 1\cong A\times A+A\quad,\\ +A\times\left(A+\bbnum 1\right)\times\left(A+\bbnum 1+\bbnum 1\right) & \cong\left(A\times A+A\right)\times\left(A+\bbnum 1+\bbnum 1\right)\\ + & \cong A\times A\times A+A\times A+A\times A\times\left(\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\\ + & \cong A\times A\times A+A\times A\times\left(\bbnum 1+\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\quad. +\end{align*} +The result looks like a polynomial in $A$, which we can now rearrange +into the required form: +\[ +A\times A\times A+A\times A\times\left(\bbnum 1+\bbnum 1+\bbnum 1\right)+A\times\left(\bbnum 1+\bbnum 1\right)\cong A\times\left(\bbnum 1+\bbnum 1+A\times\left(\bbnum 1+\bbnum 1+\bbnum 1+A\right)\right)\quad. +\] -\textbf{(a)} $\text{map}^{Z,A,B}:\text{Sel}^{Z,A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Sel}^{Z,B}\quad.$ +\textbf{(b)} Keep in mind that the conventions of the type notation +make the function arrow $\left(\rightarrow\right)$ group weaker than +other type operations. So, the type expression $\bbnum 1+A+B\rightarrow\bbnum 1\times B$ +means a function from $\bbnum 1+A+B$ to $\bbnum 1\times B$. -\textbf{(b)} $\text{flatMap}^{Z,A,B}:\text{Sel}^{Z,A}\rightarrow(A\rightarrow\text{Sel}^{Z,B})\rightarrow\text{Sel}^{Z,B}\quad.$ +Begin by using the equivalence $\bbnum 1\times B\cong B$ to obtain +$\bbnum 1+A+B\rightarrow B$. Now we use another rule: +\[ +A+B\rightarrow C\cong\left(A\rightarrow C\right)\times\left(B\rightarrow C\right) +\] +and derive the equivalence: +\[ +\bbnum 1+A+B\rightarrow B\cong\left(\bbnum 1\rightarrow B\right)\times\left(A\rightarrow B\right)\times\left(B\rightarrow B\right)\quad. +\] +Finally, we note that $\bbnum 1\rightarrow B\cong B$ and that the +type product is commutative, so we can rearrange the last type expression +into the required form: +\[ +B\times\left(A\rightarrow B\right)\times\left(B\rightarrow B\right)\cong\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B\quad. +\] +We obtain the required type expression: $\left(B\rightarrow B\right)\times\left(A\rightarrow B\right)\times B$. -\section{Discussion and further developments} +\subsubsection{Example \label{subsec:ch-solvedExample-5}\ref{subsec:ch-solvedExample-5}} -\subsection{Using the Curry-Howard correspondence for writing code} +Denote $\text{Read}^{E,A}\triangleq E\rightarrow A$ and implement +fully parametric functions with types $A\rightarrow\text{Read}^{E,A}$ +and $\text{Read}^{E,A}\rightarrow(A\rightarrow B)\rightarrow\text{Read}^{E,B}$. -The CH correspondence is used in two practically important reasoning -tasks: checking whether a type signature can be implemented as a fully -parametric function, and determining whether two types are equivalent. -For the first task, we map type expressions into formulas in the constructive -logic and apply the proof rules of that logic. For the second task, -we map type expressions into \emph{arithmetic} formulas and apply -the ordinary rules of arithmetic. +\subparagraph{Solution} -Although tools such as the \lstinline!curryhoward! library can sometimes -derive code from types, it is beneficial if a programmer is able to -derive an implementation by hand or to determine that an implementation -is impossible. For instance, the programmer should recognize that -the type signature: +Begin by defining a type alias for the type constructor $\text{Read}^{E,A}$: \begin{lstlisting} -def f[A, B]: A => (A => B) => B +type Read[E, A] = E => A \end{lstlisting} -has only one fully parametric implementation, while the following -two type signatures have none: +The first type signature has only one implementation: \begin{lstlisting} -def g[A, B]: A => (B => A) => B -def h[A, B]: ((A => B) => A) => A +def p[E, A]: A => Read[E, A] = { x => _ => x } \end{lstlisting} -Exercises in this chapter help to build up the required technique -and intuition. The two main guidelines for code derivation are: \textsf{``}values -of parametric types cannot be constructed from scratch\textsf{''} and \textsf{``}one -must hard-code the decision to return a chosen part of a disjunctive -type when no other disjunctive value is given\textsf{''}. These guidelines -can be justified by referring to the rigorous rules of proof (Table~\ref{tab:Proof-rules-for-constructive-logic}). -Sequents producing a value of type $A$ can be proved only if there -is a premise containing $A$ or a function that returns a value of -type $A$.\footnote{This is proved rigorously in the paper \texttt{\href{https://research-repository.st-andrews.ac.uk/handle/10023/8824}{https://research-repository.st-andrews.ac.uk/handle/10023/8824}} -(2016) by R.~Dyckhoff.\index{Roy Dyckhoff} See the \textsf{``}Theorem\textsf{''} -in section 6 (\textsf{``}Goal-directed pruning\textsf{''}).} One can derive a disjunction without hard-coding only if one already -has a disjunction in the premises (and then the rule \textsf{``}use \lstinline!Either!\textsf{''} -could apply). - -Throughout this chapter, we require all code to be fully parametric. -This is because the CH correspondence gives useful, non-trivial results -only for parameterized types and fully parametric code. For concrete, -non-parameterized types (\lstinline!Int!, \lstinline!String!, etc.), -one can always produce \emph{some} values even with no previous data. -So, the propositions $\mathcal{CH}(\text{Int})$ or $\mathcal{CH}(\text{String})$ -are always true. +We \emph{must} discard the argument of type $E$; we cannot use it +for computing a value of type \lstinline!A! given \lstinline!x:A!. -Consider the function \lstinline!(x:Int) => x + 1!. Its type signature, -\lstinline!Int => Int!, may be implemented by many other functions, -such as \lstinline!x => x - 1!, \lstinline!x => x * 2!, etc. So, -the type signature \lstinline!Int => Int! is insufficient to specify -the code of the function, and deriving code from that type is not -a meaningful task. Only a fully parametric type signature, such as -$A\rightarrow\left(A\rightarrow B\right)\rightarrow B$, could give -enough information for deriving the function\textsf{'}s code. Additionally, -we must require the code of functions to be fully parametric. Otherwise -we will be unable to reason about code derivation from type signatures. +The second type signature has three type parameters. It is the curried +version of the function \lstinline!map!: +\begin{lstlisting} +def map[E, A, B]: Read[E, A] => (A => B) => Read[E, B] = ??? +\end{lstlisting} +Expanding the type alias, we see that the two curried arguments are +functions of types $E\rightarrow A$ and $A\rightarrow B$. The forward +composition of these functions is a function of type $E\rightarrow B$, +or $\text{Read}^{E,B}$, which is exactly what we are required to +return. So, the code can be written as: -Validity of a ${\cal CH}$-proposition ${\cal CH}(T)$ means that -we can implement \emph{some} value of the given type $T$. But this -does not give any information about the properties of that value, -such as whether it satisfies any laws. This is why type equivalence -(which requires the laws of isomorphisms) is not determined by an -equivalence of logical formulas. - -It is useful for programmers to be able to transform type expressions -to equivalent simpler types before starting to write code. The type -notation introduced in this book is designed to help programmers to -recognize patterns in type expressions and to reason about them more -easily. We have shown that a type equivalence corresponds to \emph{each} -standard arithmetic identity such as $\left(a+b\right)+c=a+\left(b+c\right)$, -$\left(a\times b\right)\times c=a\times(b\times c)$, $1\times a=a$, -$\left(a+b\right)\times c=a\times c+b\times c$, and so on. Because -of this, we are allowed to transform and simplify types as if they -were arithmetic expressions, e.g., to rewrite: +\begin{lstlisting} +def map[E, A, B]: (E => A) => (A => B) => E => B = { r => f => r andThen f } +\end{lstlisting} +If we did not notice this shortcut, we would reason differently: We +are required to compute a value of type $B$ given \emph{three} curried +arguments $r^{:E\rightarrow A}$, $f^{:A\rightarrow B}$, and $e^{:E}$. +Write this requirement as: \[ -\bbnum 1\times\left(A+B\right)\times C+D\cong D+A\times C+B\times C\quad. +\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow???^{:B}\quad, \] -The type notation makes this reasoning more intuitive (for people -familiar with arithmetic). - -These results apply to all type expressions built up using product -types, disjunctive types (also called \textsf{``}sum\textsf{''} types because they -correspond to arithmetic sums), and function types (also called \textsf{``}exponential\textsf{''} -types because they correspond to arithmetic exponentials). Type expressions -that contain only products and sum types are called \textbf{polynomial}\index{polynomial type}\index{types!polynomial types}.\footnote{These types are often called \textsf{``}algebraic data types\index{algebraic data types}\textsf{''} -but this book prefers the more precise term \textsf{``}polynomial types\textsf{''}.} Type expressions that also contain function types may be called \textbf{exponential-polynomial}\index{exponential-polynomial type}\index{types!exponential-polynomial types}. -This book focuses on exponential-polynomial types because they are -sufficient for almost all design patterns used in functional programming. +The symbol $\text{???}^{:B}$ is called a \index{typed hole}\textbf{typed +hole}. It stands for a value that we are still figuring out how to +compute, but whose type is already known. Typed holes are supported +in Scala by an experimental compiler plugin.\footnote{See \texttt{\href{https://github.com/cb372/scala-typed-holes}{https://github.com/cb372/scala-typed-holes}}} +The plugin will print the known information about the typed hole. -There are no types corresponding to subtraction or division, so arithmetic -equations such as: -\begin{align*} -\left(1-t\right)\times\left(1+t\right) & =1-t\times t\quad,\quad\text{ and }\quad\frac{t+t\times t}{t}=1+t\quad, -\end{align*} -do not directly yield any type equivalences. However, consider this -well-known formula: +To fill the typed hole $\text{???}^{:B}$, we need a value of type +$B$. Since no arguments have type $B$, the only way of getting a +value of type $B$ is to apply $f^{:A\rightarrow B}$ to some value +of type $A$. So we write: \[ -\frac{1}{1-t}=1+t+t^{2}+t^{3}+...+t^{n}+...\quad. +\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(???^{:A})\quad. \] -At first sight, this formula appears to involve subtraction, division, -and an infinite series, and so cannot be directly translated into -a type equivalence. However, the formula can be rewritten as: -\begin{equation} -\frac{1}{1-t}=L(t)\quad\text{ where }\quad L(t)\triangleq1+t+t^{2}+t^{3}+...+t^{n}\times L(t)\quad.\label{eq:ch-example-type-formula-list} -\end{equation} -The definition of $L(t)$ is finite and only contains additions and -multiplications. So, Eq.~(\ref{eq:ch-example-type-formula-list}) -can be translated into a type equivalence: -\begin{equation} -L^{A}\cong1+A+A\times A+A\times A\times A+...+\underbrace{A\times...\times A}_{n\text{ times}}\times\,L^{A}\quad.\label{eq:ch-example-type-expansion-list} -\end{equation} -This type formula (with $n=1$) is equivalent to a recursive definition -of the type constructor \lstinline!List!: +The only way of getting an $A$ is to apply $r$ to some value of +type $E$: \[ -\text{List}^{A}\triangleq1+A\times\text{List}^{A}\quad. +\text{map}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(r(???^{:E}))\quad. \] -The type equivalence~(\ref{eq:ch-example-type-expansion-list}) suggests -that we may view the recursive type \lstinline!List! heuristically -as an \textsf{``}infinite disjunction\textsf{''} describing lists of zero, one, two, -etc., elements. +We have exactly one value of type $E$, namely $e^{:E}$. So, the +code must be: +\[ +\text{map}^{E,A,B}\triangleq r^{:E\rightarrow A}\rightarrow f^{:A\rightarrow B}\rightarrow e^{:E}\rightarrow f(r(e))\quad. +\] +Translate this to the Scala syntax: +\begin{lstlisting} +def map[E, A, B]: (E => A) => (A => B) => E => B = { r => f => e => f(r(e)) } +\end{lstlisting} +We may now notice that the expression $e\rightarrow f(r(e))$ is a +function composition $r\bef f$ applied to $e$, and simplify the +code accordingly. -\subsection{Implications for designing new programming languages} +\subsubsection{Example \label{subsec:ch-solvedExample-6}\ref{subsec:ch-solvedExample-6}} -Today\textsf{'}s functional programming practice assumes, at the minimum, that -programmers will use the six standard type constructions (Section~\ref{subsec:Type-notation-and-standard-type-constructions}) -and the eight standard code constructions (Section~\ref{subsec:The-rules-of-proof}). -These constructions are foundational in the sense that they are used -to express all design patterns of functional programming. A language -that does not directly support some of these constructions cannot -be considered a functional programming language. +Show that the type signature \lstinline!Read[A, T] => (A => B) => Read[B, T]! +cannot be implemented as a fully parametric function. -A remarkable result of the CH correspondence is that the type system -of any given programming language (functional or not) is mapped into -a \emph{certain} \emph{logic}, i.e., a system of logical operations -and proof rules. A logical operation will correspond to each of the -\emph{type} constructions available in the programming language. A -proof rule will correspond to each of the available \emph{code} constructions. -Programming languages that support all the standard type and code -constructions \textemdash{} for instance, OCaml, Haskell, F\#, Scala, -Swift, Rust, \textemdash{} are mapped into the constructive logic -with all standard logical operations available ($True$, $False$, -disjunction, conjunction, and implication). +\subparagraph{Solution} -Languages such as C, C++, Java, C\#, Go are mapped into logics that -do not have the disjunction operation or the constants $True$ and -$False$. In other words, these languages are mapped into \emph{incomplete} -logics where some true formulas cannot be proved. Incompleteness of -the logic of types will make a programming language unable to express -certain computations, e.g., directly handle data that belongs to a -disjoint domain. +Expand the type signature and try implementing this function: +\begin{lstlisting} +def m[A, B, T] : (A => T) => (A => B) => B => T = { r => f => b => ??? } +\end{lstlisting} +Given values $r^{:A\rightarrow T}$, $f^{:A\rightarrow B}$, and $b^{:B}$, +we need to compute a value of type $T$: +\[ +m=r^{:A\rightarrow T}\rightarrow f^{:A\rightarrow B}\rightarrow b^{:B}\rightarrow???^{:T}\quad. +\] +The only way of getting a value of type $T$ is to apply $r$ to some +value of type $A$: +\[ +m=r^{:A\rightarrow T}\rightarrow f^{:A\rightarrow B}\rightarrow b^{:B}\rightarrow r(???^{:A})\quad. +\] +However, we do not have any values of type $A$. We have a function +$f^{:A\rightarrow B}$ that \emph{consumes} values of type $A$, and +we cannot use $f$ to produce any values of type $A$. So, it seems +that we are unable to fill the typed hole $\text{???}^{:A}$ and implement +the function \lstinline!m!. -Languages that do not enforce type checking (e.g., Python or JavaScript) -are mapped to inconsistent logics where any proposition can be proved -\textemdash{} even propositions normally considered $False$. The -CH correspondence will map such absurd proofs to code that \emph{appears} -to compute a certain value (since the $\mathcal{CH}$-proposition -was proved to be $True$) although that value is not actually available. -In practice, such code will crash because of a value that has a wrong -type, is \textsf{``}null\textsf{''}, or is a pointer to an invalid memory location. -Those errors cannot happen in a programming language whose logic of -types is consistent and whose compiler checks all types at compile -time. +In order to verify that \lstinline!m! is unimplementable, we need +to prove that the logical formula: +\begin{equation} +\forall(\alpha,\beta,\tau).\,(\alpha\Rightarrow\tau)\Rightarrow(\alpha\Rightarrow\beta)\Rightarrow(\beta\Rightarrow\tau)\label{eq:ch-example-boolean-formula-3} +\end{equation} +is not true in the constructive logic. We could use the \lstinline!curryhoward! +library for that: +\begin{lstlisting} +@ def m[A, B, T] : (A => T) => (A => B) => B => T = implement +cmd1.sc:1: type (A => T) => (A => B) => B => T cannot be implemented +def m[A, B, T] : (A => T) => (A => B) => B => T = implement + ^ +Compilation Failed +\end{lstlisting} +Another way is to check whether this formula is true in Boolean logic. +A formula that holds in constructive logic will always hold in Boolean +logic, because all rules shown in Section~\ref{subsec:The-rules-of-proof} +preserve Boolean truth values (see Section~\ref{subsec:Relationship-between-Boolean} +for a proof). It follows that any formula that fails to hold in Boolean +logic will also not hold in constructive logic. -So, the CH correspondence gives a mathematically justified procedure -for designing new programming languages. The procedure has the following -steps: -\begin{itemize} -\item Choose a formal logic that is complete and free of inconsistencies. -\item For each logical operation, provide a type construction in the language. -\item For each axiom and proof rule of the logic, provide a code construction -in the language. -\end{itemize} -Mathematicians have studied different logics, such as modal logic, -temporal logic, or linear logic. Compared with the constructive logic, -those other logics have some additional type operations. For instance, -modal logic adds the operations \textsf{``}necessarily\textsf{''} and \textsf{``}possibly\textsf{''}, -and temporal logic adds the operation \textsf{``}until\textsf{''}. For each logic, -mathematicians have determined the minimal complete sets of operations, -axioms, and proof rules that do not lead to inconsistency. Programming -language designers can use this mathematical knowledge by choosing -a logic and translating it into a minimal \textsf{``}core\textsf{''} of a programming -language. Code in that language will be guaranteed \emph{never to -crash} as long as all types match. This mathematical guarantee (known -as \index{type safety}\textbf{type safety}) is a powerful help for -programmers since it automatically prevents a large number of coding -errors. So, programmers will benefit if they use languages designed -using the CH correspondence. +It is relatively easy to check whether a given Boolean formula is +always equal to $True$. Simplifying Eq.~(\ref{eq:ch-example-boolean-formula-3}) +with the rules of Boolean logic, we find: +\begin{align*} + & (\alpha\Rightarrow\tau)\,\gunderline{\Rightarrow}\,(\alpha\Rightarrow\beta)\,\gunderline{\Rightarrow}\,(\beta\Rightarrow\tau)\\ +{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & =\neg(\gunderline{\alpha\Rightarrow\tau})\vee\neg(\gunderline{\alpha\Rightarrow\beta})\vee(\gunderline{\beta\Rightarrow\tau})\\ +{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & =\gunderline{\neg(\neg\alpha\vee\tau)}\vee\gunderline{\neg(\neg\alpha\vee\beta)}\vee(\neg\beta\vee\tau)\\ +{\color{greenunder}\text{use de Morgan\textsf{'}s law}:}\quad & =\left(\alpha\wedge\neg\tau\right)\vee\gunderline{\left(\alpha\wedge\neg\beta\right)\vee\neg\beta}\vee\tau\\ +{\color{greenunder}\text{use identity }(p\wedge q)\vee q=q:}\quad & =\gunderline{\left(\alpha\wedge\neg\tau\right)}\vee\neg\beta\vee\gunderline{\tau}\\ +{\color{greenunder}\text{use identity }(p\wedge\neg q)\vee q=p\vee q:}\quad & =\alpha\vee\neg\beta\vee\tau\quad. +\end{align*} +This formula is not identically $True$: it is $False$ when $\alpha=\tau=False$ +and $\beta=True$. So, Eq.~(\ref{eq:ch-example-boolean-formula-3}) +is not true in Boolean logic, therefore it is also not true in constructive +logic. By the CH correspondence, we conclude that the type signature +of \lstinline!m! cannot be implemented by a fully parametric function. -Practically useful programming languages will of course need more -features than the minimal set of mathematically necessary features -derived from a chosen logic. Language designers need to make sure -that all added features are consistent with the core language. +\subsubsection{Example \label{subsec:ch-solvedExample-7}\ref{subsec:ch-solvedExample-7}} -At present, it is still not fully understood how a practical programming -language could use, say, modal or linear logic as its logic of types. -Experience suggests that, at least, the operations of the plain constructive -logic should be available. So, it appears that the six type constructions -and the eight code constructions will remain available in all future -languages of functional programming. +Define the type constructor $P^{A}\triangleq\bbnum 1+A+A$ and implement +\lstinline!map! for it: +\[ +\text{map}^{A,B}:P^{A}\rightarrow(A\rightarrow B)\rightarrow P^{B}\quad. +\] +To check that \lstinline!map! preserves information, verify the law +\lstinline!map(p)(x => x) == p! for all \lstinline!p: P[A]!. -It is possible to apply the FP paradigm while writing code in any -programming language. However, some languages lack certain features -that make FP techniques easier to use in practice. For example, in -a language such as JavaScript, Python, or Ruby, one can productively -use the map/reduce operations but not disjunctive types. More advanced -FP constructions (such as typeclasses) are impractical in these languages -because the required code becomes too hard to read and to write without -errors, which negates the advantages of rigorous reasoning about functional -programs. +\subparagraph{Solution} -Some programming languages, such as Haskell and OCaml, were designed -specifically for advanced use in the FP paradigm. Other languages, -such as F\#, Scala, Swift, PureScript, Elm, and Rust, have different -design goals but still support enough FP features to be considered -FP languages. This book uses Scala, but the same constructions may -be implemented in other FP languages in a similar way. At the level -of detail needed in this book, the differences between languages such -as OCaml, Haskell, F\#, Scala, Swift, PureScript, Elm, and Rust, do -not play a significant role. +It is implied that \lstinline!map! should be fully parametric and +information-preserving. Begin by defining a Scala type constructor +for the notation $P^{A}\triangleq\bbnum 1+A+A$: +\begin{lstlisting} +sealed trait P[A] +final case class P1[A]() extends P[A] +final case class P2[A](x: A) extends P[A] +final case class P3[A](x: A) extends P[A] +\end{lstlisting} +Now we can write code to implement the required type signature. Each +time we have several choices of an implementation, we will choose +to preserve information as much as possible. -\subsection{Practical uses of the void type (Scala\textsf{'}s \texttt{Nothing})} - -The \index{void type}void type\footnote{The \textsf{``}void\textsf{''} type as defined in this book is a type with no values. -It is \emph{not} the same as the \lstinline!void! keyword in Java -or C that denotes functions returning \textsf{``}no value\textsf{''}. Those functions -are equivalent to Scala functions returning the \lstinline!Unit! -type.} (Scala\textsf{'}s \lstinline!Nothing!) corresponds to the logical constant -$False$. (The proposition \textsf{``}\emph{the code can compute a value of -the void type}\textsf{''} is always false.) The void type is used in some -theoretical proofs but has few practical uses. One use case is for -a branch of a \lstinline!match!/\lstinline!case! expression that -throws an \index{exception}exception instead of returning a value. -In this sense, returning a value of the void type corresponds to a -crash in the program. So, a \lstinline!throw! expression is defined -as if it returns a value of type \lstinline!Nothing!. We can then -pretend to convert that \textsf{``}value\textsf{''} (which will never be actually -returned) into a value of any other type. Example~\ref{subsec:ch-Example-type-identity-0-to-A} -shows how to write a function \lstinline!absurd[A]! of type \lstinline!Nothing => A!. - -To see how this trick is used, consider this code defining a value -\lstinline!x!: \begin{lstlisting} -val x: Double = if (t >= 0.0) math.sqrt(t) else throw new Exception("error") +def map[A, B]: P[A] => (A => B) => P[B] = + p => f => p match { + case P1() => P1() // No other choice. + case P2(x) => ??? + case P3(x) => ??? + } \end{lstlisting} -The \lstinline!else! branch does not return a value, but \lstinline!x! -is declared to have type \lstinline!Double!. For this code to type-check, -both branches must return values of the same type. So, the compiler -needs to pretend that the \lstinline!else! branch also returns a -value of type \lstinline!Double!. The compiler first assigns the -type \lstinline!Nothing! to the expression \lstinline!throw ...! -and then automatically uses the conversion \lstinline!Nothing => Double! -to convert that type to \lstinline!Double!. In this way, types will -match in the definition of the value \lstinline!x!. +In the case \lstinline!P2(x)!, we are required to produce a value +of type $P^{B}$ from a value $x^{:A}$ and a function $f^{:A\rightarrow B}$. +Since $P^{B}$ is a disjunctive type with three parts, we can produce +a value of type $P^{B}$ in three different ways: \lstinline!P1()!, +\lstinline!P2(...)!, and \lstinline!P3(...)!. If we return \lstinline!P1()!, +we will lose the information about the value \lstinline!x!. If we +return \lstinline!P3(...)!, we will preserve the information about +\lstinline!x! but lose the information\index{information loss} that +the input value was a \lstinline!P2! rather than a \lstinline!P3!. +By returning \lstinline!P2(...)! in that scope, we preserve the entire +input information. -This book does not discuss exceptions in much detail. The functional -programming paradigm does not use exceptions because their presence -significantly complicates reasoning about code. +The value under \lstinline!P2(...)! must be of type $B$, and the +only way of getting a value of type $B$ is to apply $f$ to $x$. +So, we return \lstinline!P2(f(x))!. -As another example of using the void type, suppose an external library -implements a function: -\begin{lstlisting} -def parallel_run[E, A, B](f: A => Either[E, B]): Either[E, B] = ??? -\end{lstlisting} -We may imagine that \lstinline!parallel_run(f)! performs some parallel -computations using a given function $f$. In general, functions $f^{:A\rightarrow E+B}$ -may return an error of type $E$ or a result of type $B$. Suppose -we know that a particular function $f$ never fails to compute its -result. To express that knowledge in code, we may explicitly set the -type parameter $E$ to the void type \lstinline!Nothing! when applying -\lstinline!parallel_run!: +Similarly, in the case \lstinline!P3(x)!, we should return \lstinline!P3(f(x))!. +The final code of \lstinline!map! is: \begin{lstlisting} -parallel_run[Nothing, A, B](f) // Types match only when values f(a) always are of the form Right(b). +def map[A, B]: P[A] => (A => B) => P[B] = p => f => p match { + case P1() => P1() // No other choice here. + case P2(x) => P2(f(x)) // Preserve information. + case P3(x) => P3(f(x)) // Preserve information. +} \end{lstlisting} -Returning an error is now impossible (the type \lstinline!Nothing! -has no values). If the function \lstinline!parallel_run! is fully -parametric, it will work in the same way with all types $E$, including -$E=\bbnum 0$. The code implements our intention via type parameters, -giving a compile-time guarantee of correct results. - -\subsection{Relationship between Boolean logic and constructive logic\label{subsec:Relationship-between-Boolean} } - -We have seen that some true theorems of Boolean logic are not true -in constructive logic. For example, the Boolean identities $\neg\left(\neg\alpha\right)=\alpha$ -and $\left(\alpha\Rightarrow\beta\right)=(\neg\alpha\vee\beta)$ do -not hold in the constructive logic. However, as we will now show, -any theorem of constructive logic is also a theorem of Boolean logic. -The reason is that all eight rules of constructive logic (Section~\ref{subsec:The-rules-of-proof}) -are also true in Boolean logic. -To verify that a formula is true in Boolean logic, it is sufficient - to check that the value of the formula is $True$ for all possible -truth values ($True$ or $False$) of its variables. A sequent such -as $\alpha,\beta\vdash\gamma$ is true in Boolean logic if and only -if $\gamma=True$ under the assumption that $\alpha=\beta=True$. -So, the sequent $\alpha,\beta\vdash\gamma$ is translated into the -Boolean formula: +To verify the given law, we first write a matrix notation for \lstinline!map!: \[ -\alpha,\beta\vdash\gamma=\left(\left(\alpha\wedge\beta\right)\Rightarrow\gamma\right)=\left(\neg\alpha\vee\neg\beta\vee\gamma\right)\quad. +\text{map}^{A,B}\triangleq p^{:\bbnum 1+A+A}\rightarrow f^{:A\rightarrow B}\rightarrow p\triangleright\,\begin{array}{|c||ccc|} + & \bbnum 1 & B & B\\ +\hline \bbnum 1 & \text{id} & \bbnum 0 & \bbnum 0\\ +A & \bbnum 0 & f & \bbnum 0\\ +A & \bbnum 0 & \bbnum 0 & f +\end{array}\quad. \] -Table~\ref{tab:Proof-rules-of-constructive-and-boolean} translates -all proof rules of Section~\ref{subsec:The-rules-of-proof} into -Boolean formulas. The first two lines are axioms, while the subsequent -lines are Boolean theorems that can be verified by calculation. - -\begin{table} -\begin{centering} -\begin{tabular}{|c|c|} -\hline -\textbf{\small{}Constructive logic} & \textbf{\small{}Boolean logic}\tabularnewline -\hline -\hline -{\small{}$\frac{}{\Gamma\vdash{\cal CH}(\bbnum 1)}\quad(\text{create unit})$} & {\small{}$\neg\Gamma\vee True=True$}\tabularnewline -\hline -{\small{}$\frac{~}{\Gamma,\alpha\vdash\alpha}\quad(\text{use arg})$} & {\small{}$\neg\Gamma\vee\neg\alpha\vee\alpha=True$}\tabularnewline -\hline -{\small{}$\frac{\Gamma,\alpha\vdash\beta}{\Gamma\vdash\alpha\Rightarrow\beta}\quad(\text{create function})$} & {\small{}$\left(\neg\Gamma\vee\neg\alpha\vee\beta\right)=\left(\neg\Gamma\vee\left(\alpha\Rightarrow\beta\right)\right)$}\tabularnewline -\hline -{\small{}$\frac{\Gamma\vdash\alpha\quad\quad\Gamma\vdash\alpha\Rightarrow\beta}{\Gamma\vdash\beta}\quad(\text{use function})$} & {\small{}$\left(\left(\neg\Gamma\vee\alpha\right)\wedge\left(\neg\Gamma\vee\left(\alpha\Rightarrow\beta\right)\right)\right)\Rightarrow\left(\neg\Gamma\vee\beta\right)$}\tabularnewline -\hline -{\small{}$\frac{\Gamma\vdash\alpha\quad\quad\Gamma\vdash\beta}{\Gamma\vdash\alpha\wedge\beta}\quad(\text{create tuple})$} & {\small{}$\left(\neg\Gamma\vee\alpha\right)\wedge\left(\neg\Gamma\vee\beta\right)=\left(\neg\Gamma\vee\left(\alpha\wedge\beta\right)\right)$}\tabularnewline -\hline -{\small{}$\frac{\Gamma\vdash\alpha\wedge\beta}{\Gamma\vdash\alpha}\quad(\text{use tuple-}1)$} & {\small{}$\left(\neg\Gamma\vee\left(\alpha\wedge\beta\right)\right)\Rightarrow\left(\neg\Gamma\vee\alpha\right)$}\tabularnewline -\hline -{\small{}$\frac{\Gamma\vdash\alpha}{\Gamma\vdash\alpha\vee\beta}\quad(\text{create Left})$} & {\small{}$\left(\neg\Gamma\vee\alpha\right)\Rightarrow\left(\neg\Gamma\vee\left(\alpha\vee\beta\right)\right)$}\tabularnewline -\hline -{\small{}$\frac{\Gamma\vdash\alpha\vee\beta\quad\quad\Gamma,\alpha\vdash\gamma\quad\quad\Gamma,\beta\vdash\gamma}{\Gamma\vdash\gamma}\quad(\text{use Either})$} & {\small{}$\left(\left(\neg\Gamma\vee\alpha\vee\beta\right)\wedge\left(\neg\Gamma\vee\neg\alpha\vee\gamma\right)\wedge\left(\neg\Gamma\vee\neg\beta\vee\gamma\right)\right)\Rightarrow\left(\neg\Gamma\vee\gamma\right)$}\tabularnewline -\hline -\end{tabular} -\par\end{centering} -\caption{Proof rules of constructive logic are true also in the Boolean logic.\label{tab:Proof-rules-of-constructive-and-boolean}} -\end{table} - -To simplify the calculations, note that all terms in the formulas -contain the operation $\left(\neg\Gamma\vee...\right)$ corresponding -to the context $\Gamma$. Now, if $\Gamma$ is $False$, the entire -formula becomes automatically $True$, and there is nothing else to -check. So, it remains to verify the formula in case $\Gamma=True$, -and then we can simply omit all instances of $\neg\Gamma$ in the -formulas. Let us show the Boolean derivations for the rules \textsf{``}$\text{use function}$\textsf{''} -and \textsf{``}$\text{use Either}$\textsf{''}; other formulas are checked in a similar -way: +The required law is written as an equation $\text{map}\left(p\right)(\text{id})=p$, +called the\index{identity laws!of map@of \texttt{map}} \textbf{identity +law}. Substituting the code notation for \lstinline!map!, we verify +the law: \begin{align*} -{\color{greenunder}\text{formula \textsf{``}use function\textsf{''}}:}\quad & \left(\alpha\wedge\left(\alpha\Rightarrow\beta\right)\right)\Rightarrow\beta\\ -{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & =\gunderline{\neg}(\alpha\,\gunderline{\wedge}\,(\neg\alpha\,\gunderline{\vee}\,\beta))\vee\beta\\ -{\color{greenunder}\text{de Morgan\textsf{'}s laws}:}\quad & =\gunderline{\neg\alpha\vee(\alpha\wedge\neg\beta)}\vee\beta +{\color{greenunder}\text{expect to equal }p:}\quad & \text{map}\left(p\right)(\text{id})\\ +{\color{greenunder}\text{apply \texttt{map()()} to arguments}:}\quad & =p\triangleright\,\begin{array}{||ccc|} +\text{id} & \bbnum 0 & \bbnum 0\\ +\bbnum 0 & \text{id} & \bbnum 0\\ +\bbnum 0 & \bbnum 0 & \text{id} +\end{array}\\ +{\color{greenunder}\text{identity function in matrix notation}:}\quad & =p\triangleright\text{id}\\ +{\color{greenunder}\triangleright\text{-notation}:}\quad & =\text{id}\left(p\right)=p\quad. \end{align*} + + +\subsubsection{Example \label{subsec:ch-solvedExample-8}\ref{subsec:ch-solvedExample-8}} + +Implement \lstinline!map! and \lstinline!flatMap! for \lstinline!Either[L, R]!, +applied to the type parameter \lstinline!L!. + +\subparagraph{Solution} + +For a type constructor, say, $P^{A}$, the standard type signatures +for \lstinline!map! and \lstinline!flatMap! are: +\[ +\text{map}:P^{A}\rightarrow(A\rightarrow B)\rightarrow P^{B}\quad,\quad\quad\text{flatMap}:P^{A}\rightarrow(A\rightarrow P^{B})\rightarrow P^{B}\quad. +\] +If a type constructor has more than one type parameter, e.g., $P^{A,S,T}$, +one can define the functions \lstinline!map! and \lstinline!flatMap! +applied to a chosen type parameter. For example, when applied to the +type parameter $A$, the type signatures are: \begin{align*} -{\color{greenunder}\text{identity }p\vee(\neg p\wedge q)=p\vee q\text{ with }p=\neg\alpha\text{ and }q=\beta:}\quad & =\neg\alpha\vee\gunderline{\neg\beta\vee\beta}\\ -{\color{greenunder}\text{axiom \textsf{``}use arg\textsf{''}}:}\quad & =True\quad. +\text{map} & :P^{A,S,T}\rightarrow(A\rightarrow B)\rightarrow P^{B,S,T}\quad,\\ +\text{flatMap} & :P^{A,S,T}\rightarrow(A\rightarrow P^{B,S,T})\rightarrow P^{B,S,T}\quad. \end{align*} +Being \textsf{``}applied to the type parameter $A$\textsf{''} means that the other +type parameters $S,T$ in $P^{A,S,T}$ remain fixed while the type +parameter $A$ is replaced by $B$ in the type signatures of \lstinline!map! +and \lstinline!flatMap!. + +For the type \lstinline!Either[L, R]! (in the type notation, $L+R$), +we keep the type parameter $R$ fixed while $L$ is replaced by $M$. +So we obtain the type signatures: \begin{align*} -{\color{greenunder}\text{formula \textsf{``}use Either\textsf{''}}:}\quad & \left(\left(\alpha\vee\beta\right)\wedge\left(\alpha\Rightarrow\gamma\right)\wedge\left(\beta\Rightarrow\gamma\right)\right)\Rightarrow\gamma\\ -{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & =\neg\left(\left(\alpha\vee\beta\right)\wedge\left(\neg\alpha\vee\gamma\right)\wedge\left(\neg\beta\vee\gamma\right)\right)\vee\gamma\\ -{\color{greenunder}\text{de Morgan\textsf{'}s laws}:}\quad & =\left(\neg\alpha\wedge\neg\beta\right)\vee\gunderline{\left(\alpha\wedge\neg\gamma\right)}\vee\gunderline{\left(\beta\wedge\neg\gamma\right)}\vee\gamma\\ -{\color{greenunder}\text{identity }p\vee(\neg p\wedge q)=p\vee q:}\quad & =\gunderline{\left(\neg\alpha\wedge\neg\beta\right)\vee\alpha}\vee\beta\vee\gamma\\ -{\color{greenunder}\text{identity }p\vee(\neg p\wedge q)=p\vee q:}\quad & =\gunderline{\neg\alpha\vee\alpha}\vee\beta\vee\gamma\\ -{\color{greenunder}\text{axiom \textsf{``}use arg\textsf{''}}:}\quad & =True\quad. +\text{map} & :L+R\rightarrow(L\rightarrow M)\rightarrow M+R\quad,\\ +\text{flatMap} & :L+R\rightarrow(L\rightarrow M+R)\rightarrow M+R\quad. \end{align*} -Since each proof rule of the constructive logic is translated into -a true formula in Boolean logic, it follows that a proof tree in the -constructive logic will be translated into a tree of Boolean formulas -that have value $True$ for each axiom or proof rule. The result is -that any constructive proof for a sequent such as $\emptyset\vdash f(\alpha,\beta,\gamma)$ -is translated into a chain of Boolean implications that look like -this: -\[ -True=(...)\Rightarrow(...)\Rightarrow...\Rightarrow f(\alpha,\beta,\gamma)\quad. -\] -Since $\left(True\Rightarrow\alpha\right)=\alpha$, this chain proves -the Boolean formula $f(\alpha,\beta,\gamma)$. +Implementing these functions is straightforward: +\begin{lstlisting} +def map[L,M,R]: Either[L, R] => (L => M) => Either[M, R] = e => f => e match { + case Left(x) => Left(f(x)) + case Right(y) => Right(y) +} -For example, the proof tree shown in Figure~\ref{fig:Proof-of-the-sequent-example-2} -is translated into: +def flatMap[L,M,R]: Either[L, R] => (L => Either[M, R]) => Either[M, R] = e => f => e match { + case Left(x) => f(x) + case Right(y) => Right(y) +} +\end{lstlisting} +The code notation for these functions is: \begin{align*} -{\color{greenunder}\text{axiom \textsf{``}use arg\textsf{''}}:}\quad & True=\left(\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\neg\alpha\vee\alpha\right)\\ -{\color{greenunder}\text{rule \textsf{``}create function\textsf{''}}:}\quad & \quad\Rightarrow\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\alpha\right)\quad.\\ -{\color{greenunder}\text{axiom \textsf{``}use arg\textsf{''}}:}\quad & True=\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\quad.\\ -{\color{greenunder}\text{rule \textsf{``}use function\textsf{''}}:}\quad & True\Rightarrow\left(\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\beta\right)\\ -{\color{greenunder}\text{rule \textsf{``}create function\textsf{''}}:}\quad & \quad\Rightarrow\left(\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\Rightarrow\beta\right)\quad. +\text{map} & \triangleq e^{:L+R}\rightarrow f^{:L\rightarrow M}\rightarrow e\triangleright\,\begin{array}{|c||cc|} + & M & R\\ +\hline L & f & \bbnum 0\\ +R & \bbnum 0 & \text{id} +\end{array}\quad,\\ +\text{flatMap} & \triangleq e^{:L+R}\rightarrow f^{:L\rightarrow M+R}\rightarrow e\triangleright\,\begin{array}{|c||c|} + & M+R\\ +\hline L & f\\ +R & y^{:R}\rightarrow\bbnum 0^{:M}+y +\end{array}\quad. \end{align*} +Note that the code matrix for \lstinline!flatMap! cannot be split +into the $M$ and $R$ columns because we do not know in advance which +part of the disjunctive type $M+R$ will be returned when we evaluate +$f(x^{:L})$. -It is easier to check Boolean truth tables than to find a proof tree -in constructive logic (or to establish that no proof tree exists). -If we find that a formula is \emph{not} true in Boolean logic, we -know it is also not true in constructive logic. This gives us a quick -way of proving that some type signatures are \emph{not} implementable -as fully parametric functions. However, if a formula is true in Boolean -logic, it does not follow that the formula is also true in the constructive -logic. - -In addition to formulas shown in Table~\ref{tab:Logical-formulas-not-Boolean-theorems} -(Section~\ref{subsec:ch-Motivation-and-first-examples}), here are -three more examples of formulas that are \emph{not} true in Boolean -logic: -\[ -\forall\alpha.\,\alpha\quad,\quad\quad\forall(\alpha,\beta).\,\alpha\Rightarrow\beta\quad,\quad\quad\forall(\alpha,\beta).\,(\alpha\Rightarrow\beta)\Rightarrow\beta\quad. -\] -These formulas are also \emph{not} true in the constructive logic. +\subsubsection{Example \label{subsec:ch-solvedExample-9}\ref{subsec:ch-solvedExample-9}{*}} -\subsection{The constructive logic and the law of excluded middle} +Define a type constructor $\text{State}^{S,A}\equiv S\rightarrow A\times S$ +and implement the functions: -Computations in the Boolean logic are often performed using truth -tables. It is perhaps surprising that the proof rules of the constructive -logic are \emph{not} equivalent to checking whether some propositions -are $True$ or $False$ via a truth table. A general form of this -statement was proved by K.~G\"odel\index{Kurt@Kurt G\"odel} in -1932.\footnote{See \texttt{\href{https://plato.stanford.edu/entries/intuitionistic-logic-development/\#SomeEarlResu}{plato.stanford.edu/entries/intuitionistic-logic-development/}}} -In this sense, constructive logic does \emph{not} imply that every -proposition is either $True$ or $False$. This is not intuitive and -requires getting used to. Reasoning in the constructive logic must -use the axioms and derivation rules directly, instead of truth tables. +\textbf{(a)} $\text{pure}^{S,A}:A\rightarrow\text{State}^{S,A}\quad.$ -The reason Boolean logic can use truth tables is that every Boolean -proposition is either $True$ or $False$. This can be written as -the formula $\forall\alpha.\,(\neg\alpha\vee\alpha=True)$. Table~\ref{tab:Proof-rules-of-constructive-and-boolean} -uses the Boolean identity $\left(\alpha\Rightarrow\beta\right)=(\neg\alpha\vee\beta)$, -which does not hold in the constructive logic, to translate the constructive -axiom \textsf{``}$\text{use arg}$\textsf{''} into the Boolean axiom $\neg\alpha\vee\alpha=True$. -The formula $\forall\alpha.\,\neg\alpha\vee\alpha=True$ is known -as the \textbf{law of excluded middle}.\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Law_of_excluded_middle}{https://en.wikipedia.org/wiki/Law\_of\_excluded\_middle}}}\index{law of excluded middle} -It is remarkable that the constructive logic \emph{does not have} -the law of excluded middle. It is neither an axiom nor a derived theorem -in constructive logic. +\textbf{(b)} $\text{map}^{S,A,B}:\text{State}^{S,A}\rightarrow(A\rightarrow B)\rightarrow\text{State}^{S,B}\quad.$ -To see why, translate the constructive logic formula $\forall\alpha.\,\neg\alpha\vee\alpha=True$ -into a type. The negation operation ($\neg\alpha$) is defined as -the implication $\alpha\Rightarrow False$. So, the formula $\forall\alpha.\,\neg\alpha\vee\alpha$ -corresponds to the type $\forall A.\,\left(A\rightarrow\bbnum 0\right)+A$. -Can we compute a value of this type via fully parametric code? For -that, we need to compute either a value of type $A\rightarrow\bbnum 0$ -or a value of type $A$. This decision needs to be made in advance -independently of $A$, because the code of a fully parametric function -must operate in the same way for all types. As we have seen in Example~\ref{subsec:ch-Example-type-identity-A-0}, -a value of type $A\rightarrow\bbnum 0$ exists if the type $A$ is -itself $\bbnum 0$. But we do not know in advance whether $A=\bbnum 0$. -Since there are no values of type $\bbnum 0$, and the type parameter -$A$ could be, say, \lstinline!Int!, we cannot compute a value of -type $A\rightarrow\bbnum 0$. +\textbf{(c)} $\text{flatMap}^{S,A,B}:\text{State}^{S,A}\rightarrow(A\rightarrow\text{State}^{S,B})\rightarrow\text{State}^{S,B}\quad.$ -Why is it impossible to implement a value of the type $\left(A\rightarrow\bbnum 0\right)+A$? -Surely, the type $A$ is either void or not void. If $A$ is void -then $\left(A\rightarrow\bbnum 0\right)\cong\bbnum 1$ is not void -(as Example~\ref{subsec:ch-Example-type-identity-A-0} shows). So, -one of the types in the disjunction $\left(A\rightarrow\bbnum 0\right)+A$ -should be non-void and have values that we can compute. +\subparagraph{Solution} -While this argument is true, it does not help implementing a value -of type $\left(A\rightarrow\bbnum 0\right)+A$ via fully parametric -code. It is not enough to know that one of the two values \textsf{``}should -exist\textsf{''}. We need to know \emph{which} of the two values exists, and -we need to write code that computes that value. That code may not -decide what to do depending on whether the type $A$ is void, because -the code must work in the same way for all types $A$ (void or not). -As we have seen, that code is impossible to write. +It is assumed that all functions must be fully parametric and preserve +as much information as possible. We define the type alias: +\begin{lstlisting} +type State[S, A] = S => (A, S) +\end{lstlisting} -In Boolean logic, one may prove that a value \textsf{``}should exist\textsf{''} by -showing that the non-existence of a value is contradictory in some -way. However, any practically useful program needs to \textsf{``}construct\textsf{''} -(i.e., to compute) actual values. The \textsf{``}constructive\index{constructive logic}\textsf{''} -logic got its name from this requirement. So, it is the constructive -logic (not the Boolean logic) that provides correct reasoning about -the types of values computable by fully parametric functional programs. +\textbf{(a)} The type signature is $A\rightarrow S\rightarrow A\times S$, +and there is only one implementation: +\begin{lstlisting} +def pure[S, A]: A => State[S, A] = a => s => (a, s) +\end{lstlisting} +In the code notation, this is written as: +\[ +\text{pu}^{S,A}\triangleq a^{:A}\rightarrow s^{:S}\rightarrow a\times s\quad. +\] -If we drop the requirement of full parametricity, we \emph{could} -implement the law of excluded middle\index{law of excluded middle}. -Special features of Scala (reflection, type tags, and type casts) -allow programmers to compare types as values and to determine what -type was given to a type parameter when a function is applied: -\begin{lstlisting}[mathescape=true] -import scala.reflect.runtime.universe._ -def getType[T: TypeTag]: Type = weakTypeOf[T] // Convert the type parameter T into a special value. -def equalTypes[A: TypeTag, B: TypeTag]: Boolean = getType[A] =:= getType[B] // Compare types A and B. +\textbf{(b)} The type signature is: +\[ +\text{map}^{S,A,B}:(S\rightarrow A\times S)\rightarrow(A\rightarrow B)\rightarrow S\rightarrow B\times S\quad. +\] +Begin writing a Scala implementation: +\begin{lstlisting} +def map[S, A, B]: State[S, A] => (A => B) => State[S, B] = { t => f => s => ??? } +\end{lstlisting} +We need to compute a value of $B\times S$ from the curried arguments +$t^{:S\rightarrow A\times S}$, $f^{:A\rightarrow B}$, and $s^{:S}$. +We begin writing the code of \lstinline!map! using a typed hole: +\[ +\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow\text{???}^{:B}\times\text{???}^{:S}\quad. +\] +The only way of getting a value of type $B$ is by applying $f$ to +a value of type $A$: +\[ +\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow f(\text{???}^{:A})\times\text{???}^{:S}\quad. +\] +The only possibility of filling the typed hole $\text{???}^{:A}$ +is to apply $t$ to a value of type $S$. We already have such a value, +$s^{:S}$. Computing $t(s)$ yields a pair of type $A\times S$, from +which we may take the first part (of type $A$) to fill the typed +hole $\text{???}^{:A}$. The second part of the pair is a value of +type $S$ that we may use to fill the second typed hole, $\text{???}^{:S}$. +So, the Scala code is: +\begin{lstlisting}[numbers=left] +def map[S, A, B]: State[S, A] => (A => B) => State[S, B] = { + t => f => s => + val (a, s2) = t(s) + (f(a), s2) // We could also return `(f(a), s)` here. +} +\end{lstlisting} +Why not return the original value \lstinline!s! in the tuple $B\times S$, +instead of the new value \lstinline!s2!? The reason is that we would +like to preserve information as much as possible. If we return \lstinline!(f(a), s)! +in line 4, we will have discarded the computed value \lstinline!s2!, +which is a loss of information. -def excludedMiddle[A: TypeTag]: Either[A, A => Nothing] = // excludedMiddle has type ${\color{dkgreen}\forall A.\left(A\rightarrow\bbnum 0\right)+A}$. - if (equalTypes[A, Nothing]) Right((identity _).asInstanceOf[A => Nothing]) // Return ${\color{dkgreen}\text{id}:\bbnum 0\rightarrow\bbnum 0}$. - else if (equalTypes[A, Int]) Left(123.asInstanceOf[A]) // Produce some value of type Int. - else if (equalTypes[A, Boolean]) Left(true.asInstanceOf[A]) // Produce some value of type Boolean. - else ??? // Need to write many more definitions to support all other Scala types. +To write the code notation for \lstinline!map!, we need to destructure +the pair that $t(s)$ returns. We can write explicit destructuring +code like this: +\[ +\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow s^{:S}\rightarrow(a^{:A}\times s_{2}^{:S}\rightarrow f(a)\times s_{2})(t(s))\quad. +\] +If we temporarily denote by $q$ the following destructuring function: +\[ +q\triangleq(a^{:A}\times s_{2}^{:S}\rightarrow f(a)\times s_{2})\quad, +\] +we will notice that the expression $s\rightarrow q(t(s))$ is a function +composition applied to $s$. So, we rewrite $s\rightarrow q(t(s))$ +as the composition $t\bef q$ and obtain shorter code: +\[ +\text{map}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow B}\rightarrow t\bef(a^{:A}\times s^{:S}\rightarrow f(a)\times s)\quad. +\] +Shorter formulas are often easier to reason about in derivations, +although not necessarily easier to read when converted to program +code. -scala> excludedMiddle[Int] -res0: Either[Int,Int => Nothing] = Left(123) +\textbf{(c)} The required type signature is: +\[ +\text{flatMap}^{S,A,B}:(S\rightarrow A\times S)\rightarrow(A\rightarrow S\rightarrow B\times S)\rightarrow S\rightarrow B\times S\quad. +\] +We perform code reasoning with typed holes: +\[ +\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow\text{???}^{:B\times S}\quad. +\] +To fill $\text{???}^{:B\times S}$, we need to apply $f$ to some +arguments, since $f$ is the only function that returns any values +of type $B$. Applying $f$ to two values will yield a value of type +$B\times S$, just as we need: +\[ +\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow f(\text{???}^{:A})(\text{???}^{:S})\quad. +\] +To fill the new typed holes, we need to apply $t$ to an argument +of type $S$. We have only one given value $s^{:S}$ of type $S$, +so we must compute $t(s)$ and destructure it: +\[ +\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow s^{:S}\rightarrow\left(a\times s_{2}\rightarrow f(a)(s_{2})\right)(t(s))\quad. +\] +Translating this notation into Scala code, we obtain: +\begin{lstlisting} +def flatMap[S, A, B]: State[S, A] => (A => State[S, B]) => State[S, B] = { + t => f => s => + val (a, s2) = t(s) + f(a)(s2) // We could also return `f(a)(s)` here, but that would lose information. +} +\end{lstlisting} +In order to preserve information, we choose not to discard the computed +value \lstinline!s2!. -scala> excludedMiddle[Nothing] -res1: Either[Nothing,Nothing => Nothing] = Right() +The code notation for this \lstinline!flatMap! can be simplified +to: +\[ +\text{flatMap}\triangleq t^{:S\rightarrow A\times S}\rightarrow f^{:A\rightarrow S\rightarrow B\times S}\rightarrow t\bef\left(a\times s\rightarrow f(a)(s)\right)\quad. +\] + + +\subsection{Exercises\index{exercises}} + +\subsubsection{Exercise \label{subsec:ch-Exercise-0}\ref{subsec:ch-Exercise-0}} + +Find the cardinality of the following Scala type: +\begin{lstlisting} +type P = Option[Boolean => Option[Boolean]] \end{lstlisting} -In this code, we check whether $A=\bbnum 0$. If so, we can implement -$A\rightarrow\bbnum 0$ as an identity function of type $\bbnum 0\rightarrow\bbnum 0$. -Otherwise, we know that $A$ is one of the existing Scala types (\lstinline!Int!, -\lstinline!Boolean!, etc.), which are not void and have values that -we can simply write down one by one in the subsequent code. +Show that \lstinline!P! is equivalent to \lstinline!Option[Boolean] => Boolean!, +but the equivalence is accidental\index{type equivalence!accidental} +and not \textsf{``}natural\textsf{''}. -Explicit\index{type casts} \textbf{type casts}, such as \lstinline!123.asInstanceOf[A]!, -are needed because the Scala compiler cannot know that \lstinline!A! -is \lstinline!Int! in the scope where we return \lstinline!Left(123)!. -Without a type cast, the compiler will not accept \lstinline!123! -as a value of type \lstinline!A! in that scope. +\subsubsection{Exercise \label{subsec:ch-Exercise-1-a}\ref{subsec:ch-Exercise-1-a}} -The method \lstinline!asInstanceOf! is dangerous because the code -\lstinline!x.asInstanceOf[T]! disables the type checking for the -value \lstinline!x!. This tells the Scala compiler to believe that -\lstinline!x! has type \lstinline!T! even when the type \lstinline!T! -is inconsistent with the actually given code of \lstinline!x!. The -resulting programs compile but may give unexpected results or crash. -These errors would have been prevented if we did not disable the type -checking. In this book, we will avoid writing such code whenever possible.% -\begin{comment} -showing here this is equivalent in Scala just different syntax importantly -non theorems cannot be implemented in code some on theorems are statements -in logic that cannot be derived statements that are false or undereye -verbal examples of these statements are these for all a from one follows -a now this is certainly suspicious in terms of logic what if a were -false then we would have it from true false false that\textsf{'}s very obviously -wrong and we cannot implement a function of this type to implement -it we would have to take a unit argument and produce a value of type -a where a is arbitrary type but how can we produce a value of type -a of the type that we don't even know what it is and there is no data -for us to produce that value so it is impossible another example of -an impossible type is this type so from a plus B follows a if you -wanted to implement this function you would have to take a value of -disjunction type a plus B and return a value of type a but how can -you do that what exodus Junction type happens to contain B and no -a just B it cannot contain a if it contains a B it\textsf{'}s a disjunction -so then we don't have an A and then we again cannot produce any and -having a B which is a completely different arbitrary type doesn't -help us to produce me exactly the same reason shows why we cannot -produce an A a and B given a because that requires a B we cannot produce -and also this is not implementable because we are required to produce -an A but all we have is a function from A to B this function will -consume an A if given only this function cannot possibly produce an -A for us but we are required to produce an A as a result so we cannot -and also there is no proof of this formula in the logic so these examples -actually lead us to a natural question how can we decide given a certain -formula whether it is a theorem in logic and therefore whether it -can be implemented in code it is not obvious consider this example -can we write a function with this type in Scala it is not obvious -can we prove this formula it is not clear not quite obvious right -now suppose I were of the opinion that this cannot be proved but how -do I show that this cannot be proved I certainly cannot just try all -possible proofs that would be infinitely many possible proofs that -would give me all kinds of other formulas and that would give me nothing -that I can stand oh how to answer these questions so it is really -a very hard question we are not going to try to answer it on our own -we were going to use the results of mathematicians they have studied -these questions for many many years for centuries logic has been studied -since ancient Greece more than 2,000 years of study all we need to -do is to find out by what name mathematicians call this logic they -are probably already studied it what kind of logic is this that we -are using that follows from the type constructions remember and the -very beginning of our consideration we started with the type constructions -that our programming languages have so that\textsf{'}s set of type constructions -specifies the set of rules of derivation of the logic mathematicians -call this logic intuitionistic propositional logic or IPL also they -call it constructive propositional logic but it is less frequently -used most frequently used name is this and mathematicians also call -this a non classical logic because this logic is actually different -from the boolean logic that we are familiar with the logic of the -values true and false and their truth tables I assume that you are -familiar with those computations using truth tables and operations -and or not in the boolean logic so actually this logic the logic of -types as I call it or intuitionistic propositional logic is very different -from boolean logic in certain ways it\textsf{'}s similar in other ways disjunction -for instance works very differently here\textsf{'}s an example consider this -sequence if it has given that from a follows B plus C then either -from a follows B or from a follows C it sounds right from the common-sense -point of it if if B plus C Falls a B or C if I was I'm using plus -as a logical or so if B or C follows then it kind of makes sense either -B follows or C Falls indeed this is correct in the boolean logic which -we can find out by writing the truth table so we enumerate all the -possibilities for a B and C to be true or false or eight such possibilities -and for each of those possibilities we write the truth value of this -the truth value of this and we see from the table that whenever this -is true then this is also true in the boolean logic but this does -not hold in the intuitionistic logic for the logic of types well why -does it not hold that\textsf{'}s counterintuitive well in fact there is very -little that\textsf{'}s intuitive about this so-called intuitionistic logic -actually we need to think differently about this logic we need to -think can we implement an expression of this sequent so implementing -it would mean if we're given this expression we can build an expression -of this type so we're given an expression of type A to B plus C let\textsf{'}s -say some F of this type can we build an expression of this type we -can this differently by asking can we implement a function that takes -this as an argument and returns this well we know that this is equivalent -one of our derivation rules is that if you have this sequence then -you can also have a sequence that is a function type from this to -this so for the programmer it is easier to reason about a function -taking this as an argument and returning this so how can we implement -this function this function takes F and needs to return a value of -this type so the body of this function if we could implement it and -have to construct a value of type either of something there are only -two ways of constructing a value of type either one is to construct -the left value second is to construct the right value how do we decide -whether to construct the left value or the right value we have to -decide it somehow on the basis of what information can we decide it -we don't actually have any such information what we have here is a -function from a to either BC so given some value of a of type a we -could compute f of that value and then we would have either B or C -we could decide them whether to we could take them that B or that -C but that\textsf{'}s not what we need to return we don't need to return either -of BC we need to return either of this function or that function and -that function is not yet applied to any a it is it is too late for -us to ask what is the a we already have to return the left of this -or a right of that in other words this type either of something-something -is not itself a function of a it contains functions away but itself -it cannot be decided on the basis of any assets too late so we need -to supply a left or right so here right away immediately we have to -decide whether this will return a left or a right and we cannot really -decide that if we decide we return the left we must then return a -function from A to B so there\textsf{'}s no way for us to construct this function -if we're given this function because this function could sometimes -return C instead of B and then we'll be stuck we cannot do this and -we can also return we cannot also return the right either so it is -impossible to implement a function of this type implication also works -a little differently in the intuitionistic logic here\textsf{'}s an example -this holds in boolean logic but not in intuitionistic logic again -let\textsf{'}s see why how can we compute this given this this function will -give us an e only when given an argument of this type but how can -we produce a value of this type we cannot we don't have information -that will allow us to produce a value of this type a and B are some -arbitrary types remember there is universal quantifier outside of -all this for all a and for all B we're supposed to produce this and -that is impossible we don't have enough data to produce some values -type a and so we cannot implement this function conjunction works -kind of the same as in boolean logic so here\textsf{'}s an example this implemented -and this is also in boolean logic a true theorem now in boolean logic -the usual way of deciding whether something is true or something is -a theorem is to write a truth table unfortunately the intuitionistic -logic cannot have a truth table it cannot have a fixed number of truth -values even if you allow more than two truth values such that the -validity of formulas the truth of theorems can be decided on the basis -of the truth table this was shown by noodle and this means we should -not actually try to reason about this logic using truth values it -is not very useful even an infinite infinite number of truth values -will not help instead however it turns out that this logic has a decision -procedure or an algorithm and this algorithm is guaranteed either -to find the proof for any given formula of the internation intuitionistic -logic or to determine that there is no proof for that formula the -algorithm can also find several in equivalent proofs if there is a -theorem so a theorem could have several in equivalent proofs and since -each proof could be automatically translated into code of that type -it means we could generate several in equivalent expressions of some -type sometimes so that is the situation with this logic which we discover -if we write if we read papers about intuitionistic propositional logic -that are available in the literature and their open source projects -on the web such as the gen GHC which is a compiler plugin for haskell -this is another project doing the same thing and for Scala are implemented -occurred the Clary Howard library both of these Scala and Haskell -all of these color and Haskell projects do the same thing they take -a type of some expression for function and generate code for it automatic -by translating the type into sequence finding a proof in this logic -using the algorithm and translating that proof back into code in the -way that we have seen in an example it is interesting that all these -provers and there\textsf{'}s a few others there\textsf{'}s one more for the idris language -I did not mention here they all used the same decision procedure or -the same basic algorithm which is called ljt which was explained in -a paper by dick off here they all side the same paper and I believe -this is so because most other papers on this subject are unreadable -to non-specialists they are written in a very complicated way or they -describe algorithms that are too complicated so I will show how this -works in the rest of this tutorial in order to find out how to get -an algorithm we need to ask well first of all do we have the rules -of derivation that allow us to create an algorithm already here is -a summary of the axioms and the rules of derivation that we have found -so far these are direct translations of the cold expressions that -we held in the programming language in the notation of sequence now -there\textsf{'}s one other notation for derivation rules which looks like a -fraction like this the numerator is one or more sequins and the denominator -is a sequence and this notation means in order to derive what is in -the denominator you have to present proofs for what is in the numerator -so this is the convention in the literature this fraction like syntax -or notation now we keep in mind that proofs of sequence are actually -just called expressions that have these types as some variables and -this type is the entire expression so these are directly responding -to proofs of this sequence and to the proofs of these derivation rules -and so if we have a proof that operates by combining some of these -axioms and some of these generation rules which directly translate -that back into code now the question is do these rules give us an -algorithm for finding a proof the answer is no how can we use these -rules to obtain an algorithm well suppose we need to prove some sequence -like this in order to prove it we could first see if the sequence -is one of the axioms if so then we have already proved if we know -what expression to write now in this case none of the axioms match -this so much means maybe a is a times B so B here is C and then on -the Left we must have C or you must have a times B now we don't you -don't have C on the left as we have because even that\textsf{'}s not the same -we also don't have a times B at the premise we have a but we don't -have a times B so these rules don't match the other rules don't match -the premises and the goal either but also these rules so how can we -use them well when the writer must be an implication we don't have -an application on the right here we could try to delete some of the -premises because it\textsf{'}s unused well actually it doesn't look like a -good idea could you read a for example and we end up with an really -hopeless sequence from B plus C we cannot get an A ever and so but -sounds hopeless so this doesn't seem to help and changing the order -doesn't seem to help much either and so we cannot find matching rules -but actually this sequence is provable just a clever combination of -what axiom to start with and what role to use and then again some -axiom and so on it will give us that time sure because I know how -to write code for this this is not difficult you have a function with -two arguments one of them is a the other is B plus C so disjunction -of either B C and we are supposed to produce a disjunction of tuple -a B or C that\textsf{'}s easy look at this disjunction if we have a B in this -disjunction then we can produce a left of the tuple a B because we -always have an A anyway if we have a see in this disjunction then -we could return this part of the disjunction in the right of C and -we're done but unfortunately we see that the rules here do not give -us an algorithm for deciding this we need a better formulation of -the logic again mathematicians need to save us from the situation -and they have done so mathematicians have studied this logic for a -long time starting from the early 20th of the last century the first -algorithmic formulation of the logic that was found is due to Jensen -who published what he called the calculus just ignore the word calculus -it means not very much complete and sound calculus means that he came -up with some rules of derivation which are summarized here such that -they are equivalent to these they derive all the same theorems and -only the same theorems so they derive all the stuff that is right -and only that stuff they don't derive any wrong statements it\textsf{'}s very -hard to come up with such a system of axioms and derivation rules -that are equivalent to another one in this sense also it\textsf{'}s very hard -to prove that these are actually the rules that will give you all -the theorems that could be right in this logic that you can actually -derive all the theorems that are right yet work is already done by -mathematicians so we're not going to try to do it ourselves we're -just going to understand how these rules work now the syntax here -is slightly enhanced compared with this the enhancement is that their -names pretty cool now these are just labels they don't really do anything -in terms of sequence these help us identify which we all have has -been applied to which sequence and that\textsf{'}s all we do so other than -that it is the same notation so the fraction such as this one means -that there is a sequence in the denominator which we will prove if -there are proofs given for sequence in the numerator in this rule -there are two sequence of them in the numerator other rules may have -one sequence in the numerator or no sequence in the numerator so these -rules that will have no previous sequence required those are axioms -this axiom means if you have an atomic X in other words it\textsf{'}s a variable -it\textsf{'}s a type variables not not a complicated expression just attack -variable and you can derive that same variable this is our accion -right here now why is it important that this is atomic that this is -type variable and not a more complicated expression actually not important -but it\textsf{'}s the simplest rule that you can come up with and mathematicians -always like the most minimal set of rules so that\textsf{'}s why they say let\textsf{'}s -only consider this rule for the type variables X not for more complicated -expressions but we can consider this rule for any expression of course -the identity axiom well here is a truth truth axiom net which derives -the truth which is the ste symbol which I denote it by one the format -in logical notation this is the T symbol well let\textsf{'}s just call this -one for clarity so that can be derived from any premises with no previous -sequence necessary none of these other rules now what do these other -rules do they do an interesting thing actually each of these rules -is either about something in the sequence on the left to the trans -time or something in the sequence to the right of the transplant which -I here shown in blue so these are the interesting parts of the sequence -that are being worked on or transformed by the rule so here\textsf{'}s an example -this rule is actually two rules the eyes the index so I is one or -two another two rules just written for gravity like this with index -I and each of them says you will prove this if you prove one of if -you prove this so for example you will prove C given if you're given -a a one A two if you will prove C given just a one which makes sense -because if you can prove C given a one you don't need a two we can -ignore this a T we can already proved C from anyone so in this way -it would be proved and so all these rules work in this way you can -prove what\textsf{'}s on the bottom of the seat of the of the fraction if you're -given proofs for what\textsf{'}s on the top so these are eight derivation rules -and two axioms we can use this now to make a proof search how do we -do that I start with a sequence we see which rule matches that sequence -so the sequence must have something on the left and something on the -right well at least one of these it cannot be empty so it must be -something somewhere and there are only four kinds of expressions in -our logic type variables conjunctions implications and disjunctions -now notice I'm using this arithmetic arithmetic all notation for logic -just because I like it better and I will show that it has advantages -later so we take a sequence we see which rule matches one of them -won't match because either in the premise we have one of these expressions -were in the goal we have one of these expressions and then we find -the rule of match that matches we apply that rule so we now have new -sequence one or more that we will need to be proved and if they're -true then we fork the tree and now we have to prove both of them son-in -we continue doing that for each of the sequence until we hit axioms -so the tree will and this leaf or we hit a sequence to which no rule -applies in which case we cannot prove it and the entire thing is unprovable -so in the search tree there will be sequence at the nodes of the tree -and proofs will be at the edges of the tree so each node sends its -proof to the root of the tree this calculus is guaranteed by mathematicians -to be such that indeed if you cannot find a rule that applies that -means the sequence cannot be proved which was not the case here the -sequence can be proved and yet we cannot find a rule that applies -so in this calculus we can use bottom-up approach to make a proof -search as a tree here we cannot that is the advantage capitalizing -on the mathematicians results let us look at an example suppose we -want to prove this formula this theorem so first step we need to write -a sequence and this needs to be proved from no premises so we write -a sequence s0 which has an empty set of premises this is a single -now what rule applies to this sequence with your bottom up so in other -words we look at these rules and they refine which denominator matches -our sequential and our cylinders empty set on the left so all the -rules on the left cannot be applied but on the right we have an expression -which is an implication at the top level of this expression there -is this implies that so this is of the form a implies B so this rule -applies we have a sequence of the form something in our case this -is an empty set and then a implies B so we apply this rule which is -the right implication and we get a new sequence which is that what -was here before the implication is now put on the left to the trans -of the to the left of the trans time and it means that this expression -needs to be now to the left of the turnstile so now this is the sequence -s1 now we need to prove s1 well we see what rule applies to us one -well on the right there is just Q so nothing can be done of these -rules and Q is not truth so we cannot use the axiom either so let\textsf{'}s -look at their left rules on the Left we have now an implication so -this is let\textsf{'}s say a and this is B so we have a rule which has a implication -B on the left this is the row left implication let\textsf{'}s apply it that -law will give us two new sequence so these two new sequence are s2 -and s3 no these ones as you can check if you match a location B against -this implication Q so this is a this is B so then you get these two -sequence now we have to prove these two sequence as 2 and s 3 s 3 -is easy it is just the axiom of identity it is this now as 2 again -has an implication on the left let\textsf{'}s again apply the rule left implication -to that we get two more sequence as foreign s5 as for is this because -5 is this so now actually we are in trouble because as 2 and s 4 is -are the same sequence as 5 actually we could prove with some more -work but that won't help because we are in a situation when to prove -as two we need to prove again s 2 so that\textsf{'}s it that\textsf{'}s a loop that -will never give us anything it means we applied the wrong rule so -we need to backtrack this step when we apply the rule left implication -to s 2 we erase is 4 in this 5 and try a different rule to apply to -s 2 which rule can apply to s 2 well as to is this it actually has -implication on the right so we can use the right implication rule -and if we do that we get a sequence s 6 which is this and this sequence -immediately follows from the identity axiom because it has promise -are on the left and premise are and goal are on the right and that -is this axiom whatever other premises and the premise X on the left -premise X on the right and that is a type variable so that\textsf{'}s perfect -we have done the proof as 6 follows from the axiom and therefore we -have proved s0 no more sequins need to be proved and because sequence -s0 shows this to be derived from no premises than this formula is -the theorem that\textsf{'}s what the theorem means in the logic so that is -how we use this calculus to do proof search now we notice that we -were a bit stuck at some point we had a loop now if we are in the -loop we don't know what to do maybe we need to continue applying the -same rule maybe some new sequence come up or maybe we should not continue -it is not clear what to do and just looking at the rule left implication -shows us that it\textsf{'}s copying this premise a implication B it is copied -into the premises of the new sequence and so it will generate a loop -assuredly after the second time you apply it however this sequence -might be new so we might need to apply it second time we don't know -that so that is a problem it will do now there have been a lot of -work trying to fix this problem and literally decades from research -by mathematicians the main ones I found were what are the off we published -in the Soviet Union who de Meyer and dick Hoff who published in the -United States over this time discovered gradually a new set of rules -which is called ljt or the calculus ljt which cures this problem of -looping the way it clears this problem is by replacing this rule left -implication through four new rules which are listed here all other -rules are kept the same from this calculus except the rule left implication -which is replaced in what way so left implication was applying it -applied to a sequence when the sequin had an implication among the -premises or on the left to the left of the turnstile the new rules -look in more detail at what is that implication so that implication -could have one of the four expressions as the argument of the implication -it could have an atomic expression as the argument it would have a -conjunction as the argument could have a disjunction as the argument -or it could have an implication as the argument in our logic there -are no more expressions except these four atomic variables conjunctions -disjunction and implications and so we have here enumerated all the -possibilities for what could be to the left of the implication in -this premise which I have here shown in the blue in blue and so for -each of these we do certain things replacing this sequence with one -or more other sequence again it\textsf{'}s quite a lot of work to prove that -these rules are equivalent to these and also that the new rules are -somehow better they are not giving loops a lot of work which I am -NOT going to go through because that\textsf{'}s far too complicated for the -scope so what we need suffice it to say that we have very smart people -who published on this and it is reasonably sure that this is correct -so the T in the name lgt starts stands for terminating so if we use -these rules in the same way by by creating a proof tree the proof -tree will have no loops and will terminate after a finite number of -steps and there is actually this paper that is also helpful for understanding -how to implement this algorithm and this paper shows explicitly how -to construct an integer function from sequence to integers which is -a measure of the complexity of the sequence and this measure decreases -every time you apply a rule so it strictly decreases and since this -is a strictly decreasing measure on the proof tree it means that all -the next nodes in the proof tree will have a smaller value of this -measure so eventually it will hit zero and the proof tree will terminate -at that leaf either that or you have no more rules to apply and if -you have no more laws to apply then again mathematicians have proved -it means our sequence cannot be proved so this is an important result -that we are going to use and note that this this rule is quite complicated -it does a very interesting thing it takes this expression which has -implication inside an implication and it transforms this expression -in a weird way namely the B here is separated from the C by parenthesis -but here it is not separated so this transformation is highly non-trivial -and unexpected and its validity is based on this theorem that this -in the intuitionistic logic is equivalent to this equivalent means -they're both following from the other so from this promos that and -from there follows this so this key theorem was attributed to rob -you off my dick off in this paper and this is this lemma 2 which says -that if this sorry that the this derivation is if and only if that -derivations will have these two equivalences and the proof is trivial -and the 34 is a reference to to borrow be off now when a mathematician -says that something is trivial doesn't mean that a statement is easy -to understand it doesn't mean that the proof is easy to find or that -it has trees easy to understand it means none of these things it just -means that right now for this mathematician it is not interesting -to talk about how it is done that\textsf{'}s all it means could be for any -number of reasons for example mathematicians could just be lazy or -have no time to again explain this and so they say it\textsf{'}s trivial don't -be don't be deceived when you see somebody says that something is -trivial in a mathematical text so to prove this one stepping stone -could be to prove this first this is an easier theorem and if you -prove this then clearly from here you can get B to C B to C you can -substitute in here you can get a to B and then you have here a to -B so in this way you can show this equivalence in one direction now -the proof of this statement is obviously trivial in order to show -the expression of this type I will use my short notation so this is -F which has this type the first argument of the function the second -is B which is at this type then we need to produce a see how do we -produce a C we apply F to an argument of this type the argument of -this type is a function that takes a and returns a B so we take some -X of type a and we return a B which was this B so we ignore this X -we just returned that B and that\textsf{'}s the argument of F so this expression -is the proof of this sequence in other words this is the code that -has this type and therefore the proof must be available somehow so -the details of proving this theorem are left as an exercise for the -reader again when you see in a mathematical text that something is -left as an exercise for the reader it does not mean that it is easy -to do it does not mean that for you it would be a useful exercise -to do it also does not mean that the author knows how to do it it -means none of these things it just means the author doesn't feel like -doing it right now and showing it to you for whatever reason could -be because they are lazy it could be because I don't know how to do -it could be because they feel that they should know how to do it but -they don't really do know how to do it could be any of these reasons -don't be deceived when you see something like this but of course I -had to actually produce an expression function of this type in order -to implement my curry forward language because as I will show in a -moment we need to be able to implement all these has code in order -to help approver so why is that we believe the mathematicians that -the new rules are equivalent to the old rules which means that if -you find a proof using these rules somehow you should be able to find -the proof also using our initial rules which means that if you found -that proof it would easily translate that to code because each step -here is directly corresponding to a certain code expression as we -have seen at the beginning of this tutorial these cold expressions -from each of these operations so in order to do this with new rules -in other words in order to create code from proof using new rules -we need to show equivalence or we need to show how to get code out -of each of the new rules now proof of a sequence means that we have -some expression let\textsf{'}s say T what uses variables a B and C of these -types and expression itself has type G and also as I have shown this -could be conveniently seen as a function the T as a function from -a B and C from these three arguments to the type G so for each sequencing -a proof we should be able to show either that it follows from an axiom -one of these or that it show it follows from a derivation rule and -the derivations all transforms one proof into another the axioms are -just fixed expressions as we had before the axiom that actually didn't -change between our initial formulation of the logic and the new calculus -lgt they actually did not change the derivation rules changed each -new derivation rule means that you're given expressions that prove -the sequence in the numerator one or more and you are out of these -expressions somehow you have to construct an expression that proves -this sequence now when I say an expression proves the sequence what -it means is that expression has the type that is described by the -sequence it\textsf{'}s the same thing because we described types of expressions -through sequence and only those sequence that correspond to valid -and existing expressions in the programming language only those sequence -can be proved by the logic this is by construction so now we need -to just find what are these expressions that corresponds to each of -the derivation rules in each rule has a proof transformer function -as I call it and the proof transfer function is explicitly a function -that takes one or more expressions that are in the numerator and converts -that to the expression in the denominator that has this type so it -has an expression as it has an explicit function we need to write -down for each of the derivation rules so let\textsf{'}s see how this is done -for these two examples of derivation laws first example have a rule -that says if you want to derive this sequence we need to derive these -two sequence now this sequence represents an expression of type C -which uses an expression of type A plus B so let\textsf{'}s represent this -as a function from a plus B to C now we will be able to just ignore -these other premises which are common arguments and all these functions -we just pass them and we don't write them out what is the proof transformer -for this derivation rule the proof transformer for it is a function -that has two arguments t1 which is the proof of this must be a function -of type II to see and t2 which is a proof of this sequence which must -be a function of type B to see now earlier I said that sequence represent -expressions that use certain variables but equivalently we can say -these are functions that take these variables and return these expressions -that\textsf{'}s more convenient when you implement this in code so what we -need is a function that takes a to C and B to C and returns a function -from a plus B to C and this is the code that does it we take an argument -of type a plus B and we return a match expression if it\textsf{'}s in the left -we applied t1 to that value and we get to see if it\textsf{'}s in the right -we apply t2 to that value and we get a C so in any case we get a syllabus -so this is a function from a plus B to C as required another example -is the proof transformer for this rule this rule has one sequence -going to one sequence so in order to transform is proof into this -we need a function that takes argument of type A to B to C to D and -returns a function of type tuple a B going to C to D so here\textsf{'}s the -code we take a function f of type A to B to C to D we return a function -that takes a G of this type shown here in blue and return we need -to return a D so how do we get a deal we apply F to a function of -type A to B to C so we create that function out of G X of type a going -to Y of type B going to G of x1 so this is a function of type A to -B to C which is the argument of F as required and the result is of -type D so that is what we write so this kind of code is the proof -transformer for this derivation arrow and we need to produce this -proof transformers for every rule of the calculus lgt and I have done -it because I have implemented the Korea Howard library that uses LG -T so I'll must have done it for each flow this is a bit tedious because -there are many of those rules and you need to implement all this machinery -of passing arguments no matter how many in this gamma which are emitted -from this notation for brevity but in of course in the real code you -have to deal with all that too so let\textsf{'}s see how this works on an example -because once the proof tree is found we need to start backwards from -the leaves of the tree back to the root on each step we take the proof -expression apply the proof transformer to ative according to the rule -that was used on that step we get a new proof expression and so on -so for each sequence we will get a proof expression and at the end -we'll have a proof expression for the root sequence and that will -be the answer so I will denote denote by T I the proof expressions -for the sequence s hi so starting from s6 s6 was this sequence in -our proof so I mean yes just just going through the proof example -it was here backwards from a 6 back to a 0 s-six was this it followed -from axiom identity it\textsf{'}s proof expression t6 is a function of two -variables these two variables of these two types and this function -just returns the second variable so it\textsf{'}s a function of RR q and r -and just denote this by our argued and Garibaldi\textsf{'}s types r RQ variable -of this type is hard here so this function is very simple just ignores -the first argument and returns or so that is what the axiom does the -next sequence was as to as to was obtained by rule our implication -or right implication from s 6 so the proof transformer for right implication -let\textsf{'}s look at the right implication and see what the proof transformer -must be so we are given this sequence for this expression which is -the function body the function body that uses a variable of type a -somehow out of this we need to produce a function expression that -takes an argument of type a and returns that functional body so this -is the code which is just writing a new argument returning the function -body that was our proof transformer we need to convert function body -into a function so we just write that argument and arrow in the function -body so in our case we need this as a function body and so our t2 -is a function of our Q and this function is this the sequence s 3 -followed from the axiom and so it was just this function this is just -the identity function then we used the left implication so this was -actually still done in the calculus algae but the same thing works -in the calculus lgt I'm just using algae because it\textsf{'}s simpler for -example here proof transformer for the left implication is a little -more complicated and so if you look at it what what does it have to -be it takes these two expressions and returns this expression so it -takes a function from A to B to a and from B to C and it returns a -function from A to B to see how does it do it given a function a to -b you use this to derive a from it then you substitute that a into -the function into B you get a B when you use this to derive see from -that B and that\textsf{'}s your C so you use this function a to be twice you -put it in here once and then you get an A and substitute back into -the same function when you get a B then you use that and that\textsf{'}s exactly -what the proof transformer does it takes this rrq and it uses it twice -substitutes into it something that was obtained from one of the terms -and then uses the second term on the result so then this is the proof -transformer for the rule left implication the result of the proof -transformation is the proof for the sequence s1 finally we use the -right implication again which is just this function construction and -we get the proof expression for the sequence s0 now this proof expression -is written through these t1 t2 t3 we have to substitute all this back -in order to get the final expression so if we substitute first of -all we find this is our our cubone going to tea one of our cutie one -of our queue is this so we have to put it here now t3 is just identity -so we can just remove that so that gets you riq going to our Q of -T 2 T 2 is less if I have to put it in T 6 is just identity on R so -this is our going to our and so finally you have this expression so -that is the final code that has the required type notice that we have -derived this code completely algorithmic to it there was no guessing -we found which rules applied to the sequence with transformed sequence -according to the rules once we found the proof which was if we use -the calculus ljt the proof will be just a finite tree with no loops -it will terminate you can get an exhaustive depth-first search for -it for example and you find all the possible proofs if you want as -well well you will find many in any case in some for some expressions -and then we use the proof transformers which are fixed functions that -you can upfront compute for each these expressions are proof transformers -applied to the previous proofs so these are completely fixed algorithmically -fixed so we have derived this code completely algorithmically given -this expression this type so it is in this way that the career Howard -correspondence allows us to derive the code of functions from there -type signatures another important application of the correspondence -is to analyze type by some morphisms or type equivalences and I was -led to this by asking the question so in this logic or in the types -are these operations plus and times as I denoted them more like logic -more like the disjunction and conjunction or are they more like arithmetic -plus and times because this is kind of not so clear right away our -logic is this intuitionistic logic it in any case this is different -from boolean logic so what are the properties of these types really -so are the properties such that it is better to think about these -operations as plus and times rather than logical conjunction and disjunction -can answer this question I looked at identities that we have in the -water these are some identities from simple ones obvious ones to less -obvious identities like this the equal sign here stands for implication -in both directions so both this implies that and vice versa because -of this each of the implications means a function so since these are -all identities in logic it means that for example the implication -from here to here is a theorem of logic and so it can be implemented -as we know all our identities in logic can be implemented in code -and we even have an algorithm now that can automatically produce proofs -and automatically produce code so that means for any of these identities -that has some ik some expression X on the left and some Y on the right -so some kind of X equals y we have X implies Y and y implies X if -we convert that to code we will have a pair of functions function -from X to one and the function from Y to X what do these functions -do well they convert values in some ways from type X to type Y and -back so do these functions Express the equivalence of the types x -and y so that any value of type X can be converted to some equivalent -value type while and back without any loss of information is that -so that was the question I asked I looked at some examples well first -what does it mean more rigorously that types are equivalent for as -mathematicians say isomorphic the types are isomorphic and we will -use this notation for that if there is a one-to-one correspondence -between the sets of values of these types and in order to demonstrate -that we need a pair of functions one going from A to B the other going -from B to a such that the composition of these functions in both directions -is equal to identity function so F compose G or F value G will give -you from A to B and then from B to a is back so that would be identity -of a to a this will be identity of B to B if this is true if the composition -is identity it means we indeed did not lose any information let\textsf{'}s -consider an example this is an identity in the logic a conjunction -with one is equal to a in Scala the types responding to the left and -the right hand sides of this conjunction all of this are equivalent -are the conjunction of a and unit and a itself now we need functions -with these types indeed we can write functions is having these types -a pair of a and unit we need to produce an a out of that we'll just -take the first element of the pair you are done take an X of type -a will produce tuple of a and unit very easy just put a unit value -in the tuple in here done and it\textsf{'}s easy to verify that composition -of these functions will not change any values so it will be identity -in both directions another example this is an identity in logic if -this is understood as a disjunction one or a or true or a is true -that is an identity in logic for theorem in the logic are the types -equivalent though the type for 1 plus a is the option in Scala it -is option in Haskell at is called maybe this type is standard library -type in pretty much every functional programming language now option -of a is a disjunction of one or unit and a it is certainly not equivalent -to just unit because this type could contain a value of a in it but -this could not so there is no way that you could transform this type -to this and then back without losing information you could transform -so since this is a theorem you have functions from this type to this -type and back some functions you have them but these functions do -not compose to identity they cannot because what if you had a here -you must map it into unit from this unit back you must map into this -unit you cannot get an a out of unit and so that will erase this information -and that cannot become isomorphism so we see that some logic identities -do yield isomorphism types but others do not why is that let\textsf{'}s look -at some more examples to figure out why in all these examples we can -implement functions F 1 and F 2 between the two sets to two types -in both directions and then we can check we certainly can implement -them because these are logical identities but then we can check if -the compositions are identity functions and if so the types are isomorphic -but we find that in the first three examples we can do it but in this -last example we can note now I have written the logical identities -logical theorems with the arithmetic notation I call this arithmetical -notation because this suggests arithmetic operations plus and times -and if you look at these identities this looks like a well-known algebraic -identity from the school algebra in this too but this certainly seen -your own as an arithmetic as an as an arithmetic identity this is -certainly not true in arithmetic it is true in logical if you replace -this with disjunction and this with conjunction this is an identity -in logic so this suggests an interesting thing if you replace disjunction -by plus and conjunction by x and the result is an identity in arithmetic -then it is an isomorphism of types otherwise it is not let\textsf{'}s see why -this is so indeed this is so I call this the arithmetic arithmetic -oh very hard correspondence to see how it works let\textsf{'}s consider only -the types without loss of generation of generality that have a finite -set of possible values for example a boolean type has only two possible -true and false integer let\textsf{'}s say in the computers all the integers -are fine nights ago so those types have a finite set of possible values -and this does not limit our generality because in the computer everything -is finite all types have a finite set of possible values now let\textsf{'}s -consider how many values a given type has so that would be the size -of the type or using the mathematical terminology it\textsf{'}s called a cardinality -of the type so let\textsf{'}s see what is the cardinality of various type constructions -the sum type for example if the cardinality of types a and B is known -and the cardinality of a plus B the sum type the disjunction of a -and B is the sum of the two cardinalities or sizes this is because -a value of the disjunction type is constructed as either a value of -the first part or a value of the second part and so you cannot have -both together and so obviously the different number of values is just -the sum of the two sizes that the number of different values of the -sum type is just the sum of the numbers of different values of types -a and B for the product type again we have an interesting thing it\textsf{'}s -the arithmetic product of the sizes of a and B because for every a -value you could have an arbitrary B value so this is a direct product -or transient product of sets and we have school level identities about -the operations plus and times such as these identities or these all -of these identities are valid for arithmetic and they show if you -translate that into statements about the sizes of types they show -that the size of the type on the left is equal to the size of the -type on the right and that is very suggestive in other words if you -take a identity like this and you compute the size of the type on -the left and the size of the type on the right you get an arithmetic -identity of the sizes but you don't get that identity here because -the earth medical formula is not right this is very suggestive if -the sizes are equal and maybe the types are equivalent or isomorphic -when the sizes are not equal then certainly they cannot be equivalent -the function type very interestingly also is described in the same -way it provides the set of all maps between the two sets of values -so for example from integer to boolean that would be all the functions -that take some integer and return some boolean so that\textsf{'}s and a number -of boolean values \textasciicircum{} the number of integer values -that\textsf{'}s how many different functions you can have as a combinatorial -number so it\textsf{'}s an exponential and so the size of the type of function -a to be is the size of the type of B \textasciicircum{} the size of -type of a and again we have all the school identities about powers -and how to multiply powers and so on and they are directly translated -into these three identities if you take the sizes of the types on -the left and on the right the sizes will be equal due to these three -identities since the sizes are equal it\textsf{'}s very likely that the type -our actual equivalent so far haven't seen any counter examples to -this in these constructions so this gives us a meaning of the Curie -Howard correspondence so far we have seen three facets of the curly -Howard correspondence one is the correspondence between types and -logical formulas two is the correspondence between code and proofs -and three the correspondence between the cardinality of a type or -the set size of the type and the arithmetic identities that we have -in the school algebra about these types so arithmetical identities -signify type equivalence or isomorphism while logic identities only -talk about how you create some value of this type out of value of -another type so that does not guarantee that it preserves information -it just guarantees that you can implement some function of that type -it doesn't tell you that the function will be an isomorphism so if -one type is logically equivalent to another it means are equally implementable -if one is implementable another is also implementable but no more -than that whereas arithmetical identities actually tell you about -isomorphism of types therefore if you look at types and write them -using my preferred notation which is using the arithmetic all symbols -instead of logical symbols instead of these I'll use these symbols -if I do that this is very suggestive of a possible isomorphism of -types then it becomes very easy for me to reason about types I can -see right away that these two are isomorphic types or that these two -are isomorphic types because I am used to looking at school algebra -it\textsf{'}s very obvious then that this is not an isomorphism of types because -this doesn't make sense in the school algebra so reasoning about isomorphic -types is basically school level algebra involving polynomials and -powers so if you are familiar with all these identities as you should -be it will be very easy for you the reason about what types are equivalent -as long as all these types are made up of constants or primitive types -disjunctions tuples or conjunctions and functions which will then -directly be translated into exponential polynomial expressions constants -sums products and expand powers or Exponential\textsf{'}s so I call these exponential -polynomial types that is types built up from these type constructions -so all we have been talking about in this tutorial is what I call -exponential polynomial types these are the basic type constructions -that I started with tuple product function exponential disjunction -some unit constant or 1 now just one comment that in the functional -programming community today there is a terminology algebraic types -so people usually call algebraic types the types that are made from -constant types sums and products excluding Exponential\textsf{'}s I do not -find this terminology it\textsf{'}s very helpful I find it confusing because -what is particularly an algebraic about these identities these are -identities of school algebra the properties of the function type are -described by algebraic identities like this so it would be strange -to call the function type not algebraic whereas these types are algebraic -they are very similar to each other in terms of their properties being -described by identity is known from school algebra so instead of algebraic -types I would prefer to say polynomial types this is much more descriptive -and precise and if you want to talk about function types as well then -you just can you can just say exponential polynomial types or exfoli -types for short so by way of summarizing what we have done so far -what are the practical implications of the career Howard correspondence -so one set of implications is actually for writing code and reason -and eternal code one thing we can do now is if we're given a function -with some type and usually this will be typed with type parameters -all type trainers fully parametric types such as the function we have -been considering here all these functions do not have any types that -are specific like integer or string all the types are fully parametric -and then there are some constructions some type expressions made out -of these types so these are what I call fully parametric functions -for these functions we have a decision procedure an algorithm that -based on the ljt calculus which decides whether this function can -be implemented in code and computer scientists a type is inhabited -if you can produce a value of this type in your program so CH of T -is this proposition which they call type is inhabited and I prefer -to call it just that you can compute a value of this type or code -has the type O code can create a value of this type and so we have -a algorithm that can also generate the code from type when it is possible -if it is not possible the algorithm will tell you so often not always -but often this algorithm can be used actually to generate the code -you want we can also use what I call the arithmetic of glory Harvard -correspondence to reason about type isomorphisms and to transform -types isomorphic we simplify type expressions just like we simplify -expressions in school level algebra by expanding brackets by permuting -the order of terms like a plus B is equal to B plus a or associativity -a times B all times C can be expanded and so on so this allows us -once we have written types in the short notation in the notation that -I prefer which resembles school algebra because it uses the plus and -times symbols instead of the logic symbols so once we rewrite our -types and this notation which I have been doing consistently in this -tutorial it enables us the reason very easily but which types are -equal or isomorphic because we are all familiar with the school level -algebra what are the problems that we cannot solve using this knowledge -one thing we cannot do is to generate code automatically such that -it will be an isomorphism so for instance in an example here we are -able to generate automatically the code of these functions but it -will not be an isomorphism and the lgt algorithm cannot check that -this is nice a morphism that\textsf{'}s the important thing this algorithm -does not know about equations or isomorphisms it only knows that it -found some code that has the type you wanted whether this code is -useful to you or not we don't know the algorithm doesn't know this -also if the algorithm finds several such several proofs of a sequence -it will generate several not in equivalent versions of your code it -doesn't know which one is is useful maybe some of them are useless -maybe not the algorithm cannot automatically decide that in general -another thing we cannot do is to express complicated conditions via -types such as that array is sorted the type system is not powerful -enough in all the languages I listed you need a much more powerful -type system such as that in the programming language interests or -add them or cook those are much more powerful type systems that can -express such complicated conditions but for those type systems there -is no algorithm that will generate code another thing we cannot do -is to generate code that has type constructors such as the map function -here\textsf{'}s an example in Scala this is a map function on a list so there\textsf{'}s -the list of a a is a type parameter and then we say dot map and map -has another type frame to be it takes a function from A to B for any -B so a is fixed but now from any B we can take a function from A to -B and generate a list of B so if we wrote this formula in the short -type notation this would look something like this I'm writing subscript -a because this is a type parameter so this is like an argument or -a type parameter I'm writing it like this and then from this this -is the first argument of the function and then there is a second argument -which is this F and that is another quantifier for B inside parentheses -so this formula has a quantifier inside so far we have been dealing -with formulas that have all quantifiers outside and so we never write -quantifiers explicitly but here we have to write them inside this -is a more powerful logic which is called first-order logic in other -words this is a logic where you have quantifiers anywhere in the formula -including inside the formula unfortunately this logic is undecidable -so there is no algorithm that we can use either to find the proof -and therefore code freedom type or to show that there is no proof -no code so we're kind of stuck in all these directions some more remarks -about the curry Harvard correspondence first is that only with parameterize -types we can get some interesting information out of it if we take -concrete types like integer then the proposition CH event meaning -that our code can have a value of type int it that\textsf{'}s always true can -always write any some integer value we don't need any previous data -for it so for all specific types all these propositions are always -choice completely void of information the only interesting part comes -when we start considering type variables if we start asking can we -make a type which is either of a B going to a going to B in soon for -all a B once we start doing this with type parameters a B and so on -then we get interesting information as we have seen in this tutorial -another remark is that functions like this one are not sufficiently -described by their type so that this is the type of integer going -to integer now looking at this type we can put this into a sequence -but we'll never get enough information to actually get this function -so only certain class of functions which are fully typed biometric -their type signature is informative enough so that we can derive code -automatically only in much more powerful type systems you can have -type information that is enough to specify fully a code like this -another caveat is that I don't know the proof that arithmetic identity -guarantees the type equivalence it is certainly a necessary condition -because if two types have different cardinality or different size -of their sets of values that they cannot be equivalent or they cannot -be isomorphic so this is a necessary condition but it\textsf{'}s not a sufficient -condition it looks like I don't know if this is sufficient I haven't -seen any counter examples so far final remarks about type correspondence -the logical constant false did not appear in any of my slides so far -this was on purpose it has extremely limited practical use in programming -languages because actually we have types corresponding to false Scala -has type called nothing Haskell has type usually called void that -corresponds to the logical constant false what does it mean CH of -nothing is false it means your code can never have a value of type -nothing or in Haskell void you can never compute a value of this type -so clearly it has a very limited practical significance you will never -be able to compute any values of this type ever in any program it\textsf{'}s -identically falseness this constant so if you want to add it to the -logic it\textsf{'}s very easy you just have one rule and you're not done you -can derive things with it if you want but they will have almost never -any use in practical code also we did not talk about negation none -of the calculus calculate that I should have in logical negation as -in operation again for the same reason we do not have a programming -language construction that represents logical negation negation by -definition is like this is an application from 8 to 4 so that\textsf{'}s not -a not a means from a follows falsehood now since you cannot ever get -false in a programming language you cannot really implement this function -in any useful sense and so i have seen some haskell library that used -this type void as a type parameter in some way but certainly it\textsf{'}s -a very limited and rare use and so it is not really lumen 18 to include -negation it could probably find some very esoteric uses of it but -almost never useful and finally there is another set of important -implications from the Kurihara correspondence these are implications -for people who want to design new programming languages as we have -seen the Karaka with correspondence maps the type system of a programming -language into a certain logical system where prepositions follow from -each other or can be proved from each other and this enables us to -reason about programmed to see what kind of code can be written if -some other kind of code can be written and logical reasoning is very -powerful it\textsf{'}s simpler than trying to write code and it gives you algorithms -and all kinds of mathematical results that have been found over the -centuries so languages like those listed here have all the five type -constructions that I wasted in the beginning of this tutorial and -mapping them into logic gives a full constructive logic or full intuitionistic -logic with all logical operations and or so conjunction disjunction -implication and the truth constant whereas languages such as C C++ -Java and c-sharp and so on they're mapped to incomplete logics because -they do not have some of these operations for instance they do not -have type constructions of correspond to disjunction we also do not -have the true constant or the false constant so they are mapped to -a logic that lacks some of the foundational logical operation so it -can be only fewer theorems can be proved in that logic and so your -reasoning about theory types is hampered languages called scripting -languages sometimes such as Python or JavaScript will be and so on -also our belongs there in that line those languages only have one -type they actually don't check types at compile time and so they're -mapped to logics with only one proposition those logics are extremely -small in terms of what kind of things you can reason about and so -if you write a program in these languages you are completely unable -to reason at the level of types whereas in these languages you are -able to reason but in a limited way you're not having a complete logic -so this suggests a principle for designing the type system in a new -programming language the first step would be to choose a good and -complete logic that is free of inconsistency mathematicians have studied -all kinds of logics and they are always interested in questions such -as is this logic consistent consistent means you cannot derive false -from true is this logic complete can you derive all things that are -true are there enough axioms and rules of derivation or maybe there -are too many axioms and rules of derivation you can delete some of -them and have fewer mathematicians have always been interested in -such questions they found all kinds of interesting logics where you -can derive a lot of interesting theorems non trivial theorems and -they found the minimum sets of axioms and rules of derivations for -these logics use their results take one of the logics that they do -them and develop such as intuitionistic logic model logic temporal -logic linear logic and so on take one of these logics for each of -the basic operations of this logic provide type constructions in your -programming language that are easy to use for instance your logic -has disjunction implication or something else provide a type constructor -for each of them that\textsf{'}s easy to use easy to write down such as provided -by the languages we have seen then every type will be mapped to a -logical form of the OPF logical formula for every type and there will -be a type for every logical formula and then for each rule of the -new logic for each derivation rule there should be a construct in -the code that corresponds to it so that you could transform proofs -in logic into code and code into proofs if you do that your language -will be faithful to the scorecard correspondence you will be able -to use logic to reason about your language and one important result -at this level while we have seen that you can sometimes generate code -that is maybe nice but a very important result is that if your logic -is free of inconsistency it means that no program will ever be able -to derive an inconsistent an inconsistent type means that you had -a function that requires some type a but it was called with a different -type beam which is incompatible and that basically crashes so in languages -like C and C++ we have all kinds of crashes like a segmentation fault -in Java the exceptions nullpointerexception or class cast exception -which happens when you call a function on the wrong type of argument -and that happens if your logic is inconsistent if your logic can derive -incorrect statements from correct premises then if you translate that -derivation into code and the that code will derive incompatible type -at the wrong place and it will crash the crash will happen at runtime -the compiler will not catch this inconsistency because the compiler -only checks the logic of types and the logic checks out you have followed -the rules of derivation of the logic the compiler can check out all -these logical rules but the compiler does not know that your logic -is inconsistent maybe and then it will deep have derived an inconsistent -result falsehood from truth for example and that will crash at runtime -now we know that crashing at runtime is not a good outcome so in fact -languages like Oh camel have been studied and for other languages -some subsets of Haskell I believe called safe Haskell have been studied -and it has been shown that they cannot crash and they're the way to -show it mathematically is to use the fact that they are based on a -complete and consistent logic and then all you need to show is that -your compiler does not have some critical bugs that allow it to oversee -that you have not followed the derivation rules of the logic that -is an extremely valuable feature of functional programming languages -that are based on the Curie habit correspondence you can prove their -safety at compile time or at least exclude a large number of possible -bugs and errors certainly these languages are quite large and they -include features that are not covered by the Carey Hart correspondence -type constructors that I have not considered in this tutorial and -those might may not be safe but at least the foundation of these languages -the foundation of the type system will be safe so that is the final -lesson from the great Howard correspondence this concludes the tutorial -\end{comment} +Verify the type equivalences $A+A\cong\bbnum 2\times A$ and $A\times A\cong\bbnum 2\rightarrow A$, +where $\bbnum 2$ denotes the \lstinline!Boolean! type. + +\subsubsection{Exercise \label{subsec:ch-Exercise-1}\ref{subsec:ch-Exercise-1}} + +Show that $A\Rightarrow(B\vee C)\neq(A\Rightarrow B)\wedge(A\Rightarrow C)$ +in constructive and Boolean logic. + +\subsubsection{Exercise \label{subsec:ch-solvedExample-5-1}\ref{subsec:ch-solvedExample-5-1}} + +Verify the type equivalence $\left(A\rightarrow B\times C\right)\cong\left(A\rightarrow B\right)\times\left(A\rightarrow C\right)$ +with full proofs. + +\subsubsection{Exercise \label{subsec:ch-Exercise-type-identity-4}\ref{subsec:ch-Exercise-type-identity-4}} + +Use known rules to verify the type equivalences without need for proofs: + +\textbf{(a)} $\left(A+B\right)\times\left(A\rightarrow B\right)\cong A\times\left(A\rightarrow B\right)+\left(\bbnum 1+A\rightarrow B\right)\quad.$ + +\textbf{(b)} $\left(A\times(\bbnum 1+A)\rightarrow B\right)\cong\left(A\rightarrow B\right)\times\left(A\rightarrow A\rightarrow B\right)\quad.$ + +\textbf{(c)} $A\rightarrow\left(\bbnum 1+B\right)\rightarrow C\times D\cong\left(A\rightarrow C\right)\times\left(A\rightarrow D\right)\times\left(A\times B\rightarrow C\right)\times\left(A\times B\rightarrow D\right)\quad.$ + +\subsubsection{Exercise \label{subsec:ch-Exercise-2}\ref{subsec:ch-Exercise-2}} + +Write the type notation for \lstinline!Either[(A, Int), Either[(A, Char), (A, Float)]]!. +Transform this type into an equivalent type of the form $A\times(...)$. + +\subsubsection{Exercise \label{subsec:ch-Exercise-3}\ref{subsec:ch-Exercise-3}} + +Define a type $\text{OptE}^{T,A}\triangleq\bbnum 1+T+A$ and implement +information-preserving \lstinline!map! and \lstinline!flatMap! for +it, applied to the type parameter $A$. Get the same result using +the equivalent type $(\bbnum 1+A)+T$, i.e., \lstinline!Either[Option[A], T]!. +The required type signatures are: +\begin{align*} +\text{map}^{A,B,T} & :\text{OptE}^{T,A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{OptE}^{T,B}\quad,\\ +\text{flatMap}^{A,B,T} & :\text{OptE}^{T,A}\rightarrow(A\rightarrow\text{OptE}^{T,B})\rightarrow\text{OptE}^{T,B}\quad. +\end{align*} + + +\subsubsection{Exercise \label{subsec:ch-Exercise-4}\ref{subsec:ch-Exercise-4}} + +Implement the \lstinline!map! function for the type constructor \lstinline!P[A]! +from Example~\ref{subsec:ch-solvedExample-2}. The required type +signature is $P^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow P^{B}$. +Preserve information as much as possible. + +\subsubsection{Exercise \label{subsec:ch-Exercise-4-1}\ref{subsec:ch-Exercise-4-1}} + +For the type constructor $Q^{T,A}$ defined in Exercise~\ref{subsec:Exercise-type-notation-1}, +define the \lstinline!map! function, preserving information as much +as possible: +\[ +\text{map}^{T,A,B}:Q^{T,A}\rightarrow\left(A\rightarrow B\right)\rightarrow Q^{T,B}\quad. +\] + + +\subsubsection{Exercise \label{subsec:Exercise-disjunctive-6}\ref{subsec:Exercise-disjunctive-6}} + +Define a recursive type constructor $\text{Tr}_{3}$ as $\text{Tr}_{3}{}^{A}\triangleq\bbnum 1+A\times A\times A\times\text{Tr}_{3}{}^{A}$ +and implement the \lstinline!map! function for it, with the standard +type signature: $\text{map}^{A,B}:\text{Tr}_{3}{}^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Tr}_{3}{}^{B}\quad.$ +\subsubsection{Exercise \label{subsec:ch-Exercise-5}\ref{subsec:ch-Exercise-5}} -\subsection{The LJT algorithm\label{app:The-LJT-algorithm}} +Implement fully parametric, information-preserving functions with +the types: -In this chapter, we have seen how to derive code from a type signature -as long as one has a proof of the sequent corresponding to that type -signature. But Section~\ref{subsec:Example:-Proving-a-ch-proposition} -gave an example showing that the rules in Table~\ref{tab:Proof-rules-of-constructive-and-boolean} -do not provide an algorithm for finding a proof for a given sequent. +\textbf{(a)} $Z+A\times A\rightarrow(A\rightarrow B)\rightarrow Z+B\times B\quad.$ -To illustrate this problem on another example, let us try proving -the sequent: -\[ -A,B\vee C\vdash(A\wedge B)\vee C\quad. -\] -We expect that this sequent is provable because we can write the corresponding -Scala code: +\textbf{(b)} $A+Z\rightarrow B+Z\rightarrow(A\rightarrow B\rightarrow C)\rightarrow C+Z\quad.$ + +\textbf{(c)} $P+A\times A\rightarrow(A\rightarrow B)\rightarrow(P\rightarrow A+Q)\rightarrow Q+B\times B\quad.$ + +\textbf{(d)} $\text{flatMap}^{E,A,B}:\text{Read}^{E,A}\rightarrow(A\rightarrow\text{Read}^{E,B})\rightarrow\text{Read}^{E,B}\quad.$ + +\textbf{(e)} $\text{State}^{S,A}\rightarrow\left(S\times A\rightarrow B\right)\rightarrow\text{State}^{S,B}\quad.$ + +\subsubsection{Exercise \label{subsec:ch-Exercise-7}\ref{subsec:ch-Exercise-7}{*}} + +Denote $\text{Cont}^{R,T}\triangleq\left(T\rightarrow R\right)\rightarrow R$ +and implement the functions: + +\textbf{(a)} $\text{map}^{R,T,U}:\text{Cont}^{R,T}\rightarrow(T\rightarrow U)\rightarrow\text{Cont}^{R,U}\quad.$ + +\textbf{(b)} $\text{flatMap}^{R,T,U}:\text{Cont}^{R,T}\rightarrow(T\rightarrow\text{Cont}^{R,U})\rightarrow\text{Cont}^{R,U}\quad.$ + +\subsubsection{Exercise \label{subsec:ch-Exercise-8}\ref{subsec:ch-Exercise-8}{*}} + +Denote $\text{Sel}^{Z,T}\triangleq\left(T\rightarrow Z\right)\rightarrow T$ +and implement the functions: + +\textbf{(a)} $\text{map}^{Z,A,B}:\text{Sel}^{Z,A}\rightarrow\left(A\rightarrow B\right)\rightarrow\text{Sel}^{Z,B}\quad.$ + +\textbf{(b)} $\text{flatMap}^{Z,A,B}:\text{Sel}^{Z,A}\rightarrow(A\rightarrow\text{Sel}^{Z,B})\rightarrow\text{Sel}^{Z,B}\quad.$ + +\section{Discussion and further developments} + +\subsection{Using the Curry-Howard correspondence for writing code} + +The CH correspondence is used in two practically important reasoning +tasks: checking whether a type signature can be implemented as a fully +parametric function, and determining whether two types are equivalent. +For the first task, we map type expressions into formulas in the constructive +logic and apply the proof rules of that logic. For the second task, +we map type expressions into \emph{arithmetic} formulas and apply +the ordinary rules of arithmetic. + +Although tools such as the \lstinline!curryhoward! library can sometimes +derive code from types, it is beneficial if a programmer is able to +derive an implementation by hand or to determine that an implementation +is impossible. For instance, the programmer should recognize that +the type signature: \begin{lstlisting} -def f[A, B, C](a: A): Either[B, C] => Either[(A, B), C] = { - case Left(b) => Left((a, b)) - case Right(c) => Right(c) -} +def f[A, B]: A => (A => B) => B \end{lstlisting} -How can we obtain a proof of this sequent according to the rules in -Table~\ref{tab:Proof-rules-of-constructive-and-boolean}? We find -that we could potentially apply the rules \textsf{``}create \lstinline!Left!\textsf{''}, -\textsf{``}create \lstinline!Right!\textsf{''}, \textsf{``}use \lstinline!Either!\textsf{''}, and -\textsf{``}use function\textsf{''}. However, no matter what rule we choose, we will -get stuck at the next step. Let us see why: - -To apply \textsf{``}create \lstinline!Left!\textsf{''}, we first need to prove the -sequent $A,B\vee C\vdash A\wedge B$. But this sequent cannot be proved: -we do not necessarily have values of both types $A$ and $B$ if we -are only given values of type $A$ and of type \lstinline!Either[B, C]!. -To apply \textsf{``}create \lstinline!Right!\textsf{''}, we need to prove the sequent -$A,B\vee C\vdash C$. Again, we find that this sequent cannot be proved. -The next choice is the rule \textsf{``}use \lstinline!Either!\textsf{''} that matches -any goal of the sequent as the proposition $\gamma$. But we are then -required to choose two new propositions ($\alpha$ and $\beta$) such -that we can prove $A,B\vee C\vdash\alpha\vee\beta$ as well as $A,B\vee C,\alpha\vdash(A\wedge B)\vee C$ -and $A,B\vee C,\beta\vdash(A\wedge B)\vee C$. It is not clear how -we should choose $\alpha$ and $\beta$ in order to make progress -in the proof. The remaining rule, \textsf{``}use function\textsf{''}, similarly requires -us to choose a new proposition $\alpha$ such that we can prove $A,B\vee C\vdash\alpha$ -and $A,B\vee C\vdash\alpha\Rightarrow((A\wedge B)\vee C)$. The rules -give us no guidance for choosing $\alpha$ appropriately. +has only one fully parametric implementation, while the following +two type signatures have none: +\begin{lstlisting} +def g[A, B]: A => (B => A) => B +def h[A, B]: ((A => B) => A) => A +\end{lstlisting} +Exercises in this chapter help to build up the required technique +and intuition. The two main guidelines for code derivation are: \textsf{``}values +of parametric types cannot be constructed from scratch\textsf{''} and \textsf{``}one +must hard-code the decision to return a chosen part of a disjunctive +type when no other disjunctive value is given\textsf{''}. These guidelines +can be justified by referring to the rigorous rules of proof (Table~\ref{tab:Proof-rules-for-constructive-logic}). +Sequents producing a value of type $A$ can be proved only if there +is a premise containing $A$ or a function that returns a value of +type $A$.\footnote{This is proved rigorously by R.~Dyckhoff\index{Roy Dyckhoff} as +the \textsf{``}Theorem\textsf{''} in section 6 (\textsf{``}Goal-directed pruning\textsf{''}), see +\texttt{\href{https://research-repository.st-andrews.ac.uk/handle/10023/8824}{https://research-repository.st-andrews.ac.uk/handle/10023/8824}}} One can derive a disjunction without hard-coding only if one already +has a disjunction in the premises (and then the rule \textsf{``}use \lstinline!Either!\textsf{''} +could apply). -To see that the rules in Table~\ref{tab:Proof-rules-for-constructive-logic} -are not helpful for proof search, note that the rules \textsf{``}use function\textsf{''} -and \textsf{``}use \lstinline!Either!\textsf{''} require us to choose new unknown -propositions and to prove more complicated sequents than the ones -we had before. For instance, the rule \textsf{``}use function\textsf{''} gives a proof -of $\Gamma\vdash\beta$ only if we first choose some other proposition -$\alpha$ and prove the sequents $\Gamma\vdash\alpha$ and $\Gamma\vdash\alpha\Rightarrow\beta$. -The rule does not say how to choose the proposition $\alpha$ correctly. -We need to guess the correct $\alpha$ by trial and error. Even after -choosing $\alpha$ in some way, we will have to prove a more complicated -sequent ($\Gamma\vdash\alpha\Rightarrow\beta$). It is not guaranteed -that we are getting closer to finding the proof of the initial sequent -($\Gamma\vdash\beta$). +Throughout this chapter, we require all code to be fully parametric. +This is because the CH correspondence gives useful, non-trivial results +only for parameterized types and fully parametric code. For concrete, +non-parameterized types (\lstinline!Int!, \lstinline!String!, etc.), +one can always produce \emph{some} values even with no previous data. +So, the propositions $\mathcal{CH}(\text{Int})$ or $\mathcal{CH}(\text{String})$ +are always true. -It is far from obvious how to overcome that difficulty. Mathematicians -have studied the constructive logic for more than 60 years, trying -to replace the rules in Table~\ref{tab:Proof-rules-of-constructive-and-boolean} -by a different but equivalent set of derivation rules that require -no guessing when looking for a proof. The first partial success came -in 1935 with an algorithm called \textsf{``}LJ\textsf{''}.\footnote{See \texttt{\href{https://en.wikipedia.org/wiki/Sequent_calculus\#Overview}{https://en.wikipedia.org/wiki/Sequent\_calculus\#Overview}}} -The LJ algorithm still has a significant problem: one of its derivation -rules may be applied infinitely many times. So, the LJ algorithm is -not guaranteed to terminate without some heuristics for avoiding an -infinite loop. A terminating version of the LJ algorithm, called \index{LJT algorithm}LJT, -was formulated in 1992.\footnote{The history of that research is described in \texttt{\href{https://research-repository.st-andrews.ac.uk/handle/10023/8824}{https://research-repository.st-andrews.ac.uk/handle/10023/8824}}. -An often cited paper by R.~Dyckhoff\index{Roy Dyckhoff} is found -at \texttt{\href{https://philpapers.org/rec/DYCCSC}{https://philpapers.org/rec/DYCCSC}}} +Consider the function \lstinline!(x:Int) => x + 1!. Its type signature, +\lstinline!Int => Int!, may be implemented by many other functions, +such as \lstinline!x => x - 1!, \lstinline!x => x * 2!, etc. So, +the type signature \lstinline!Int => Int! is insufficient to specify +the code of the function, and deriving code from that type is not +a meaningful task. Only a fully parametric type signature, such as +$A\rightarrow\left(A\rightarrow B\right)\rightarrow B$, could give +enough information for deriving the function\textsf{'}s code. Additionally, +we must require the code of functions to be fully parametric. Otherwise +we will be unable to reason about code derivation from type signatures. -We will first present the LJ algorithm. Although that algorithm does -not guarantee termination, it is simpler to understand and to apply -by hand. Then we will show how to modify the LJ algorithm in order -to obtain the always-terminating LJT algorithm. +Validity of a ${\cal CH}$-proposition ${\cal CH}(T)$ means that +we can implement \emph{some} value of the given type $T$. But this +does not give any information about the properties of that value, +such as whether it satisfies any laws. This is why type equivalence +(which requires the laws of isomorphisms) is not determined by an +equivalence of logical formulas. -\subsubsection*{The LJ algorithm} +It is useful for programmers to be able to transform type expressions +to equivalent simpler types before starting to write code. The type +notation introduced in this book is designed to help programmers to +recognize patterns in type expressions and to reason about them more +easily. We have shown that a type equivalence corresponds to \emph{each} +standard arithmetic identity such as $\left(a+b\right)+c=a+\left(b+c\right)$, +$\left(a\times b\right)\times c=a\times(b\times c)$, $1\times a=a$, +$\left(a+b\right)\times c=a\times c+b\times c$, and so on. Because +of this, we are allowed to transform and simplify types as if they +were arithmetic expressions, e.g., to rewrite: +\[ +\bbnum 1\times\left(A+B\right)\times C+D\cong D+A\times C+B\times C\quad. +\] +The type notation makes this reasoning more intuitive (for people +familiar with arithmetic). -Figure~\ref{fig:Rules-of-the-LJ-algorithm} shows the LJ algorithm\textsf{'}s -axioms and derivation rules. Each rule says that the bottom sequent -will be proved if proofs are given for sequent(s) at the top. For -each possible sub-expression (conjunction $X\wedge Y$, disjunction -$X\vee Y$, and implication $X\Rightarrow Y$) there is one rule where -that sub-expression is a premise (at \textsf{``}left\textsf{''}) and one rule where -that sub-expression is the goal (at \textsf{``}right\textsf{''}). Those sub-expressions -are shown in blue in Figure~\ref{fig:Rules-of-the-LJ-algorithm} -to help us look for a proof. To find out which rules apply, we match -some part of the sequent with a blue sub-expression. +These results apply to all type expressions built up using product +types, disjunctive types (also called \textsf{``}sum\textsf{''} types because they +correspond to arithmetic sums), and function types (also called \textsf{``}exponential\textsf{''} +types because they correspond to arithmetic exponentials). Type expressions +that contain only products and sum types are called \textbf{polynomial}\index{polynomial type}\index{types!polynomial types}.\footnote{These types are often called \textsf{``}algebraic data types\index{algebraic data types}\textsf{''} +but this book prefers the more precise term \textsf{``}polynomial types\textsf{''}.} Type expressions that also contain function types are called \textbf{exponential-polynomial}\index{exponential-polynomial type}\index{types!exponential-polynomial types}. +We focus on exponential-polynomial types because they are sufficient +for almost all design patterns used in functional programming. -\begin{figure} -\begin{centering} -\fbox{\begin{minipage}[t]{0.7\linewidth}% +There are no types corresponding to subtraction or division, so arithmetic +equations such as: \begin{align*} -\frac{}{\Gamma,X\vdash{\color{blue}X}}~(\text{Id})\qquad & \qquad\frac{}{\Gamma\vdash{\color{blue}\top}}~(\text{True})\\ -\frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\Rightarrow B}\vdash C}~(\text{Left}\Rightarrow)\qquad & \qquad\frac{\Gamma,A\vdash B}{\Gamma\vdash{\color{blue}A\Rightarrow B}}~(\text{Right}\Rightarrow)\\ -\frac{\Gamma,A_{i}\vdash C}{\Gamma,{\color{blue}A_{1}\wedge A_{2}}\vdash C}~(\text{Left}\wedge_{i})\qquad & \qquad\frac{\Gamma\vdash A\quad\quad\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\wedge B}}~(\text{Right}\wedge)\\ -\frac{\Gamma,A\vdash C\quad\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\vee B}\vdash C}~(\text{Left}\vee)\qquad & \qquad\frac{\Gamma\vdash A_{i}}{\Gamma\vdash{\color{blue}A_{1}\vee A_{2}}}~(\text{Right}\vee_{i}) +\left(1-t\right)\times\left(1+t\right) & =1-t\times t\quad,\quad\text{ and }\quad\frac{t+t\times t}{t}=1+t\quad, \end{align*} -% -\end{minipage}} -\par\end{centering} -\caption{Axioms and derivation rules of the LJ algorithm. Each of the rules -\textquotedblleft ($\text{Left}\wedge_{i}$)\textquotedblright{} and -\textquotedblleft ($\text{Right}\vee_{i}$)\textquotedblright{} have -two versions, with $i=1$ or $i=2$. \label{fig:Rules-of-the-LJ-algorithm}} -\end{figure} +do not directly yield any type equivalences. However, consider this +well-known formula: +\[ +\frac{1}{1-t}=1+t+t^{2}+t^{3}+...+t^{n}+...\quad. +\] +At first sight, this formula appears to involve subtraction, division, +and an infinite series, and so cannot be directly translated into +a type equivalence. However, the formula can be rewritten as: +\begin{equation} +\frac{1}{1-t}=L(t)\quad\text{ where }\quad L(t)\triangleq1+t+t^{2}+t^{3}+...+t^{n}\times L(t)\quad.\label{eq:ch-example-type-formula-list} +\end{equation} +The definition of $L(t)$ is finite and only contains additions and +multiplications. So, Eq.~(\ref{eq:ch-example-type-formula-list}) +can be translated into a type equivalence: +\begin{equation} +L^{A}\cong1+A+A\times A+A\times A\times A+...+\underbrace{A\times...\times A}_{n\text{ times}}\times\,L^{A}\quad.\label{eq:ch-example-type-expansion-list} +\end{equation} +This type formula (with $n=1$) is equivalent to a recursive definition +of the type constructor \lstinline!List!: +\[ +\text{List}^{A}\triangleq1+A\times\text{List}^{A}\quad. +\] +The type equivalence~(\ref{eq:ch-example-type-expansion-list}) suggests +that we may view the recursive type \lstinline!List! heuristically +as an \textsf{``}infinite disjunction\textsf{''} describing lists of zero, one, two, +etc., elements. + +\subsection{Implications for designing new programming languages} + +Today\textsf{'}s functional programming practice assumes, at the minimum, that +programmers will use the six standard type constructions (Section~\ref{subsec:Type-notation-and-standard-type-constructions}) +and the eight standard code constructions (Section~\ref{subsec:The-rules-of-proof}). +These constructions are foundational in the sense that they are used +to express all design patterns of functional programming. A language +that does not directly support some of these constructions cannot +be considered a functional programming language. + +A remarkable result of the CH correspondence is that the type system +of any given programming language (functional or not) is mapped into +a \emph{certain} \emph{logic}, i.e., a system of logical operations +and proof rules. A logical operation will correspond to each of the +\emph{type} constructions available in the programming language. A +proof rule will correspond to each of the available \emph{code} constructions. +Programming languages that support all the standard type and code +constructions \textemdash{} for instance, OCaml, Haskell, F\#, Scala, +Swift, Rust, \textemdash{} are mapped into the constructive logic +with all standard logical operations available ($True$, $False$, +disjunction, conjunction, and implication). + +Languages such as C, C++, Java, C\#, Go are mapped into logics that +do not have the disjunction operation or the constants $True$ and +$False$. In other words, these languages are mapped into \emph{incomplete} +logics where some true formulas cannot be proved. Incompleteness of +the logic of types will make a programming language unable to express +certain computations, e.g., directly handle data that belongs to a +disjoint domain. + +Languages that do not enforce type checking (e.g., Python or JavaScript) +are mapped to inconsistent logics where any proposition can be proved +\textemdash{} even propositions normally considered $False$. The +CH correspondence will map such absurd proofs to code that \emph{appears} +to compute a certain value (since the $\mathcal{CH}$-proposition +was proved to be $True$) although that value is not actually available. +In practice, such code will crash because of a value that has a wrong +type, is \textsf{``}null\textsf{''}, or is a pointer to an invalid memory location. +Those errors cannot happen in a programming language whose logic of +types is consistent and whose compiler checks all types at compile +time. -It turns out that the rules in Figure~\ref{fig:Rules-of-the-LJ-algorithm} -are \emph{equivalent} to the rules in Table~\ref{tab:Proof-rules-for-constructive-logic}. -The proof is beyond the scope of this book. We only remark that this -equivalence is far from obvious. To prove it, one needs to demonstrate -that any sequent derived through the first set of rules is also derivable -through the second set, and vice versa. +So, the CH correspondence gives a mathematically justified procedure +for designing new programming languages. The procedure has the following +steps: +\begin{itemize} +\item Choose a formal logic that is complete and free of inconsistencies. +\item For each logical operation, provide a type construction in the language. +\item For each axiom and proof rule of the logic, provide a code construction +in the language. +\end{itemize} +Mathematicians have studied different logics, such as modal logic, +temporal logic, or linear logic. Compared with the constructive logic, +those other logics have some additional type operations. For instance, +modal logic adds the operations \textsf{``}necessarily\textsf{''} and \textsf{``}possibly\textsf{''}, +and temporal logic adds the operation \textsf{``}until\textsf{''}. For each logic, +mathematicians have determined the minimal complete sets of operations, +axioms, and proof rules that do not lead to inconsistency. Programming +language designers can use this mathematical knowledge by choosing +a logic and translating it into a minimal \textsf{``}core\textsf{''} of a programming +language. Code in that language will be guaranteed \emph{never to +crash} as long as all types match. This mathematical guarantee (known +as \index{type safety}\textbf{type safety}) is a powerful help for +programmers since it automatically prevents a large number of coding +errors. So, programmers will benefit if they use languages designed +using the CH correspondence. -To illustrate the LJ algorithm, let us prove the sequent~(\ref{eq:ch-example-sequent-2}). -Denote that sequent by $S_{0}$: -\[ -S_{0}\triangleq\emptyset\vdash\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\Rightarrow\beta\quad. -\] - Since the goal of $S_{0}$ contains an implication, we use the rule -\textsf{``}($\text{Right}\Rightarrow$)\textsf{''} and get a sequent $S_{1}$: -\[ -S_{1}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\beta\quad. -\] -Now the implication is in the premise, so we use the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''} -and get two new sequents: -\[ -S_{2}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\alpha\Rightarrow\alpha\quad,\quad\quad S_{3}\triangleq\beta\vdash\beta\quad. -\] -Sequent $S_{3}$ follows from the \textsf{``}(Id)\textsf{''} axiom, so it remains -to prove $S_{2}$. Since $S_{2}$ contains an implication both as -a premise and as the goal, we may apply either the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''} -or the rule \textsf{``}($\text{Right}\Rightarrow$)\textsf{''}. We choose to apply -\textsf{``}($\text{Left}\Rightarrow$)\textsf{''} and get two new sequents: -\[ -S_{4}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\vdash\alpha\Rightarrow\alpha\quad,\quad\quad S_{5}:\beta\vdash\alpha\Rightarrow\alpha\quad. -\] -Notice that $S_{4}=S_{2}$. So, our proof search is getting into an -infinite loop trying to prove the same sequent $S_{2}$ over and over -again. We can prove $S_{5}$ but this will not help us break the loop. +Practically useful programming languages will of course need more +features than the minimal set of mathematically necessary features +derived from a chosen logic. Language designers need to make sure +that all added features are consistent with the core language. -Once we recognize the problem, we backtrack to the point where we -chose to apply \textsf{``}($\text{Left}\Rightarrow$)\textsf{''} to $S_{2}$. That -was a bad choice, so let us instead apply \textsf{``}($\text{Right}\Rightarrow$)\textsf{''} -to $S_{2}$. This yields a new sequent $S_{6}$: -\[ -S_{6}\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta,\alpha\vdash\alpha\quad. -\] -This sequent follows from the \textsf{``}(Id)\textsf{''}axiom. There are no more sequents -to prove, so the proof of $S_{0}$ is finished. It can be drawn as -a \index{proof tree}\textbf{proof tree} like this: -\[ -\xymatrix{\xyScaleY{1.5pc}\xyScaleX{4pc} & & & (\text{Id})\\ -\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[r]\sp(0.5){S_{2}}\ar[ru]\sp(0.6){S_{3}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){S_{6}} & (\text{Id}) -} -\] -The nodes of the proof tree are axioms or derivation rules, and the -edges are intermediate sequents required by the rules. Some rule nodes -branch into several sequents because some rules require more than -one new sequent to be proved. The leaves of the tree are axioms that -do not require proving any further sequents. +At present, it is still not fully understood how a practical programming +language could use, say, modal or linear logic as its logic of types. +Experience suggests that, at least, the operations of the plain constructive +logic should be available. So, it appears that the six type constructions +and the eight code constructions will remain available in all future +languages of functional programming. -\subsubsection*{Extracting code from proofs} +It is possible to apply the FP paradigm while writing code in any +programming language. However, some languages lack certain features +that make FP techniques easier to use in practice. For example, in +a language such as JavaScript, Python, or Ruby, one can productively +use the map/reduce operations but not disjunctive types. More advanced +FP constructions (such as typeclasses) are impractical in these languages +because the required code becomes too hard to read and to write without +errors, which negates the advantages of rigorous reasoning about functional +programs. -According to the Curry-Howard correspondence, a sequent (such as $A,B,...,C\vdash X$) -represents the task of writing a fully parametric code expression -of type $X$ that uses some given values of types $A$, $B$, ..., -$C$. The sequent is true (i.e., can be proved) if that code expression -can be found. So, the code serves as an \textsf{``}evidence of proof\textsf{''} for -the sequent. +Some programming languages, such as Haskell and OCaml, were designed +specifically for advanced use in the FP paradigm. Other languages, +such as F\#, Scala, Swift, PureScript, Elm, and Rust, have different +design goals but still support enough FP features to be considered +FP languages. This book uses Scala, but the same constructions may +be implemented in other FP languages in a similar way. At the level +of detail needed in this book, the differences between languages such +as OCaml, Haskell, F\#, Scala, Swift, PureScript, Elm, and Rust, do +not play a significant role. -\begin{figure} -\begin{centering} -\noindent\fbox{\begin{minipage}[t]{1\columnwidth - 2\fboxsep - 2\fboxrule}% -\begin{align*} -\frac{}{\Gamma,A\vdash{\color{blue}A}}~(\text{Id})\quad & \quad\text{Proof}\,(\Gamma,A\vdash A)_{\text{given }p^{:\Gamma},x^{:A}}=x\\ -\frac{}{\Gamma\vdash{\color{blue}\top}}~(\text{True})\quad & \quad\text{Proof}\,(\Gamma\vdash\top)_{\text{given }p^{:\Gamma}}=1\\ -\frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\Rightarrow B}\vdash C}~(\text{Left}\Rightarrow)\quad & \quad\text{Proof}\,(\Gamma,A\Rightarrow B\vdash C)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p,b^{:B}}\\ - & \quad\quad\text{where}\quad b^{:B}\triangleq q\big(\text{Proof}\,(\Gamma,A\Rightarrow B\vdash A)_{\text{given }p,q}\big)\\ -\frac{\Gamma,A\vdash B}{\Gamma\vdash{\color{blue}A\Rightarrow B}}~(\text{Right}\Rightarrow)\quad & \quad\text{Proof}\,(\Gamma\vdash A\Rightarrow B)_{\text{given }p^{:\Gamma}}=x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash B)_{\text{given }p^{:\Gamma},x^{:A}}\\ -\frac{\Gamma,A\vdash C}{\Gamma,{\color{blue}A\wedge B}\vdash C}~(\text{Left}\wedge_{1})\quad & \quad\text{Proof}\,(\Gamma,A\wedge B\vdash C)_{\text{given }p^{:\Gamma},(a^{:A}\times b^{:B})}\\ - & \quad\quad=\text{Proof}\,(\Gamma,A\vdash C)_{\text{given }p^{:\Gamma},a^{:A}}\\ -\frac{\Gamma,B\vdash C}{\Gamma,{\color{blue}A\wedge B}\vdash C}~(\text{Left}\wedge_{2})\quad & \quad\text{Proof}\,(\Gamma,A\wedge B\vdash C)_{\text{given }p^{:\Gamma},(a^{:A}\times b^{:B})}\\ - & \quad\quad=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p^{:\Gamma},b^{:B}}\\ -\frac{\Gamma\vdash A\quad\quad\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\wedge B}}~(\text{Right}\wedge)\quad & \quad\text{Proof}\,(\Gamma\vdash A\wedge B)_{\text{given }p^{:\Gamma}}\\ - & \quad\quad=\text{Proof}\,(\Gamma\vdash A)_{\text{given }p^{:\Gamma}}\times\text{Proof}\,(\Gamma\vdash B)_{\text{given }p^{:\Gamma}}\\ -\frac{\Gamma,A\vdash C\quad\quad\Gamma,B\vdash C}{\Gamma,{\color{blue}A\vee B}\vdash C}~(\text{Left}\vee)\quad & \quad\text{Proof}\,(\Gamma,A\vee B\vdash C)_{\text{given }p^{:\Gamma},q^{:A+B}}\\ - & \quad\quad=q\triangleright\begin{array}{|c||c|} - & C\\ -\hline A & x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash C)_{\text{given }p,x}\\ -B & y^{:B}\rightarrow\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p,y} -\end{array}\\ -\frac{\Gamma\vdash A}{\Gamma\vdash{\color{blue}A\vee B}}~(\text{Right}\vee_{1})\quad & \quad\text{Proof}\,(\Gamma\vdash A\vee B)_{\text{given }p^{:\Gamma}}=\text{Proof}\,(\Gamma\vdash A)+\bbnum 0^{:B}\\ -\frac{\Gamma\vdash B}{\Gamma\vdash{\color{blue}A\vee B}}~(\text{Right}\vee_{2})\quad & \quad\text{Proof}\,(\Gamma\vdash A\vee B)_{\text{given }p^{:\Gamma}}=\bbnum 0^{:A}+\text{Proof}\,(\Gamma\vdash B) -\end{align*} -% -\end{minipage}} -\par\end{centering} -\caption{\label{fig:proof-transformers-for-LJ-rules}Proof transformers for -the rules of the LJ algorithm.} -\end{figure} +\subsection{Practical uses of the void type (Scala\textsf{'}s \texttt{Nothing})} -In the previous subsection, we have found a proof of the sequent $S_{0}$, -which represents the task of writing a fully parametric function with -type signature $(\left(A\rightarrow A\right)\rightarrow B)\rightarrow B$). -Let us now see how we can extract the code of that function from the -proof of the sequent $S_{0}$. +The \index{void type}void type\footnote{The \textsf{``}void\textsf{''} type as defined in this book is a type with no values. +It is \emph{not} the same as the \lstinline!void! keyword in Java +or C that denotes functions returning \textsf{``}no value\textsf{''}. Those functions +are equivalent to Scala functions returning the \lstinline!Unit! +type.} (Scala\textsf{'}s \lstinline!Nothing!) corresponds to the logical constant +$False$. (The proposition \textsf{``}\emph{the code can compute a value of +the void type}\textsf{''} is always false.) The void type is used in some +theoretical proofs but has few practical uses. One use case is for +a branch of a \lstinline!match!/\lstinline!case! expression that +throws an \index{exception}exception instead of returning a value. +In this sense, returning a value of the void type corresponds to a +crash in the program. So, a \lstinline!throw! expression is defined +as if it returns a value of type \lstinline!Nothing!. We can then +pretend to convert that \textsf{``}value\textsf{''} (which will never be actually +returned) into a value of any other type. Example~\ref{subsec:ch-Example-type-identity-0-to-A} +shows how to write a function \lstinline!absurd[A]! of type \lstinline!Nothing => A!. -We start from the leaves of the proof tree and move step by step towards -the initial sequent. At each step, we shorten the proof tree by replacing -some sequent by its corresponding evidence-of-proof code. Eventually -we will replace the initial sequent by its corresponding code. Let -us see how this procedure works for the proof tree of the sequent -$S_{0}$ shown in the previous section. +To see how this trick is used, consider this code defining a value +\lstinline!x!: +\begin{lstlisting} +val x: Double = if (t >= 0.0) math.sqrt(t) else throw new Exception("error") +\end{lstlisting} +The \lstinline!else! branch does not return a value, but \lstinline!x! +is declared to have type \lstinline!Double!. For this code to type-check, +both branches must return values of the same type. So, the compiler +needs to pretend that the \lstinline!else! branch also returns a +value of type \lstinline!Double!. The compiler first assigns the +type \lstinline!Nothing! to the expression \lstinline!throw ...! +and then automatically uses the conversion \lstinline!Nothing => Double! +to convert that type to \lstinline!Double!. In this way, types will +match in the definition of the value \lstinline!x!. -Since the leaves are axioms, let us write the code corresponding to -each axiom of LJ: -\begin{align*} - & \frac{}{\Gamma,X\vdash X}~(\text{Id})\quad:\quad\quad\text{Proof}\,(\Gamma,X\vdash X)_{\text{given }p^{:\Gamma},x^{:X}}=x\quad;\\ - & \frac{}{\Gamma\vdash\top}~(\text{True})\quad:\quad\quad\text{Proof}\,(\Gamma\vdash\top)_{\text{given }p^{:\Gamma}}=1\quad. -\end{align*} -Here we denote explicitly the values (such as $p$ and $x$) given -as premises to the sequent. The notation $p^{:\Gamma}$ means all -values given in the set of premises $\Gamma$. Below we will assume -that the propositions $\alpha$ and $\beta$ correspond to types $A$ -and $B$; that is, $\alpha\triangleq{\cal CH}(A)$ and $\beta\triangleq{\cal CH}(B)$. +This book does not discuss exceptions in much detail. The functional +programming paradigm does not use exceptions because their presence +significantly complicates reasoning about code. -The leaves in the proof tree for $S_{0}$ are the \textsf{``}($\text{Id}$)\textsf{''} -axioms used to prove the sequents $S_{3}$ and $S_{6}$. Let us write -the code that serves as the \textsf{``}evidence of proof\textsf{''} for these sequents. -For brevity, we denote $\gamma\triangleq\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta$ -and $C\triangleq\left(A\rightarrow A\right)\rightarrow B$, so that -$\gamma={\cal CH}(C)$. Then we can write: -\begin{align*} - & S_{3}\triangleq\beta\vdash\beta\quad,\quad\quad\text{Proof}\,(S_{3})_{\text{given }y^{:B}}=y\quad,\\ - & S_{6}\triangleq\gamma,\alpha\vdash\alpha\quad,\quad\quad\text{Proof}\,(S_{6})_{\text{given }q^{:C},x^{:A}}=x\quad. -\end{align*} -Note that the proof of $S_{6}$ does not use the first given value -$q^{:C}$ (corresponding to the premise $\gamma$). +As another example of using the void type, suppose an external library +implements a function: +\begin{lstlisting} +def parallel_run[E, A, B](f: A => Either[E, B]): Either[E, B] = ??? +\end{lstlisting} +We may imagine that \lstinline!parallel_run(f)! performs some parallel +computations using a given function $f$. In general, functions $f^{:A\rightarrow E+B}$ +may return an error of type $E$ or a result of type $B$. Suppose +we know that a particular function $f$ never fails to compute its +result. To express that knowledge in code, we may explicitly set the +type parameter $E$ to the void type \lstinline!Nothing! when applying +\lstinline!parallel_run!: +\begin{lstlisting} +parallel_run[Nothing, A, B](f) // Types match only when values f(a) always are of the form Right(b). +\end{lstlisting} +Returning an error is now impossible (the type \lstinline!Nothing! +has no values). If the function \lstinline!parallel_run! is fully +parametric, it will work in the same way with all types $E$, including +$E=\bbnum 0$. The code implements our intention via type parameters, +giving a compile-time guarantee of correct results. -We now shorten the proof tree by replacing the sequents $S_{3}$ and -$S_{6}$ by their \textsf{``}evidence of proof\textsf{''}: +So far, none of our examples involved the logical \textbf{negation}\index{negation (in logic)} +operation. It is defined as: \[ -\xymatrix{\xyScaleY{1.5pc}\xyScaleX{4pc} & & & \square\\ -\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[r]\sp(0.5){S_{2}}\ar[ru]\sp(0.6){(y)_{\text{given }y^{:B}}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){(x)_{\text{given }q^{:C},x^{:A}}} & \square -} +\neg\alpha\triangleq(\alpha\Rightarrow False)\quad. \] +Its practical use is as limited as that of $False$ and the void type. +However, logical negation plays an important role in Boolean logic, +which we will discuss next. -The next step is to consider the proof of $S_{2}$, which is found -by applying the rule \textsf{``}($\text{Right}\Rightarrow$)\textsf{''}. This rule -promises to give a proof of $S_{2}$ if we have a proof of $S_{6}$. -In order to extract code from that rule, we can write a function that -transforms a proof of $S_{6}$ into a proof of $S_{2}$. We call this -function the \textbf{proof transformer}\index{Curry-Howard correspondence!proof transformer}\index{proof transformer} -corresponding to the rule \textsf{``}($\text{Right}\Rightarrow$)\textsf{''}. That -rule and its transformer are defined as: -\[ -\frac{\Gamma,A\vdash B}{\Gamma\vdash A\Rightarrow B}~(\text{Right}\Rightarrow)\quad:\quad\quad\text{Proof}\,(\Gamma\vdash A\Rightarrow B)_{\text{given }p^{:\Gamma}}=x^{:A}\rightarrow\text{Proof}\,(\Gamma,A\vdash B)_{\text{given }p^{:\Gamma},x^{:A}}\quad. -\] -Applying the proof transformer to the known proof of $S_{6}$, we -obtain a proof of $S_{2}$: -\[ -\text{Proof}\,(S_{2})_{\text{given }q^{:C}}=x^{:A}\rightarrow\text{Proof}\,(S_{6})_{\text{given }q^{:C},x^{:A}}=(x^{:A}\rightarrow x)_{\text{given }q^{:C}}\quad. -\] -The proof tree can be now shortened to: +\subsection{Relationship between Boolean logic and constructive logic\label{subsec:Relationship-between-Boolean} } + +We have seen that some true theorems of Boolean logic are not true +in constructive logic. For example, the Boolean identities $\neg\left(\neg\alpha\right)=\alpha$ +and $\left(\alpha\Rightarrow\beta\right)=(\neg\alpha\vee\beta)$ do +not hold in the constructive logic. However, as we will now show, +any theorem of constructive logic is also a theorem of Boolean logic. +The reason is that all eight rules of constructive logic (Section~\ref{subsec:The-rules-of-proof}) +are also true in Boolean logic. + +To verify that a formula is true in Boolean logic, it is sufficient + to check that the value of the formula is $True$ for all possible +truth values ($True$ or $False$) of its variables. A sequent such +as $\alpha,\beta\vdash\gamma$ is true in Boolean logic if and only +if $\gamma=True$ under the assumption that $\alpha=\beta=True$. +So, the sequent $\alpha,\beta\vdash\gamma$ is translated into the +Boolean formula: \[ -\xymatrix{\xyScaleY{1.5pc}\xyScaleX{3.5pc} & & & \square\\ -\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){S_{1}} & (\text{Left}\Rightarrow)\ar[rr]\sp(0.62){(x^{:A}\rightarrow x)_{\text{given }q^{:C}}}\ar[ru]\sp(0.6){(y)_{\text{given }y^{:B}}} & & \square -} +\alpha,\beta\vdash\gamma=\left(\left(\alpha\wedge\beta\right)\Rightarrow\gamma\right)=\left(\neg\alpha\vee\neg\beta\vee\gamma\right)\quad. \] +Table~\ref{tab:Proof-rules-of-constructive-and-boolean} translates +all proof rules of Section~\ref{subsec:The-rules-of-proof} into +Boolean formulas. The first two lines are axioms, while the subsequent +lines are Boolean theorems that can be verified by calculation. -The next step is to get the proof of $S_{1}$ obtained by applying -the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''}. That rule requires two -previous sequents, so its transformer is a function of two previously -obtained proofs: +\begin{table} +\begin{centering} +\begin{tabular}{|c|c|} +\hline +\textbf{\small{}Constructive logic} & \textbf{\small{}Boolean logic}\tabularnewline +\hline +\hline +{\small{}$\frac{}{\Gamma\vdash{\cal CH}(\bbnum 1)}\quad(\text{create unit})$} & {\small{}$\neg\Gamma\vee True=True$}\tabularnewline +\hline +{\small{}$\frac{~}{\Gamma,\alpha\vdash\alpha}\quad(\text{use arg})$} & {\small{}$\neg\Gamma\vee\neg\alpha\vee\alpha=True$}\tabularnewline +\hline +{\small{}$\frac{\Gamma,\alpha\vdash\beta}{\Gamma\vdash\alpha\Rightarrow\beta}\quad(\text{create function})$} & {\small{}$\left(\neg\Gamma\vee\neg\alpha\vee\beta\right)=\left(\neg\Gamma\vee\left(\alpha\Rightarrow\beta\right)\right)$}\tabularnewline +\hline +{\small{}$\frac{\Gamma\vdash\alpha\quad\Gamma\vdash\alpha\Rightarrow\beta}{\Gamma\vdash\beta}\quad(\text{use function})$} & {\small{}$\left(\left(\neg\Gamma\vee\alpha\right)\wedge\left(\neg\Gamma\vee\left(\alpha\Rightarrow\beta\right)\right)\right)\Rightarrow\left(\neg\Gamma\vee\beta\right)$}\tabularnewline +\hline +{\small{}$\frac{\Gamma\vdash\alpha\quad\Gamma\vdash\beta}{\Gamma\vdash\alpha\wedge\beta}\quad(\text{create tuple})$} & {\small{}$\left(\neg\Gamma\vee\alpha\right)\wedge\left(\neg\Gamma\vee\beta\right)=\left(\neg\Gamma\vee\left(\alpha\wedge\beta\right)\right)$}\tabularnewline +\hline +{\small{}$\frac{\Gamma\vdash\alpha\wedge\beta}{\Gamma\vdash\alpha}\quad(\text{use tuple-}1)$} & {\small{}$\left(\neg\Gamma\vee\left(\alpha\wedge\beta\right)\right)\Rightarrow\left(\neg\Gamma\vee\alpha\right)$}\tabularnewline +\hline +{\small{}$\frac{\Gamma\vdash\alpha}{\Gamma\vdash\alpha\vee\beta}\quad(\text{create Left})$} & {\small{}$\left(\neg\Gamma\vee\alpha\right)\Rightarrow\left(\neg\Gamma\vee\left(\alpha\vee\beta\right)\right)$}\tabularnewline +\hline +{\small{}$\frac{\Gamma\vdash\alpha\vee\beta\quad\Gamma,\alpha\vdash\gamma\quad\Gamma,\beta\vdash\gamma}{\Gamma\vdash\gamma}\quad(\text{use Either})$} & {\small{}$\left(\neg\Gamma\vee\alpha\vee\beta\right)\wedge\left(\neg\Gamma\vee\neg\alpha\vee\gamma\right)\quad\quad$}\tabularnewline + & $\quad\quad\wedge\left(\neg\Gamma\vee\neg\beta\vee\gamma\right)\Rightarrow\left(\neg\Gamma\vee\gamma\right)$\tabularnewline +\hline +\end{tabular} +\par\end{centering} +\caption{Proof rules of constructive logic are true also in the Boolean logic.\label{tab:Proof-rules-of-constructive-and-boolean}} +\end{table} + +To simplify the calculations, note that all terms in the formulas +contain the operation $\left(\neg\Gamma\vee...\right)$ corresponding +to the context $\Gamma$. Now, if $\Gamma$ is $False$, the entire +formula becomes automatically $True$, and there is nothing else to +check. So, it remains to verify the formula in case $\Gamma=True$, +and then we can simply omit all instances of $\neg\Gamma$ in the +formulas. Let us show the Boolean derivations for the rules \textsf{``}$\text{use function}$\textsf{''} +and \textsf{``}$\text{use Either}$\textsf{''}; other formulas are checked in a similar +way: \begin{align*} - & \frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,A\Rightarrow B\vdash C}~(\text{Left}\Rightarrow)\quad:\\ - & \text{Proof}\,(\Gamma,A\Rightarrow B\vdash C)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}=\text{Proof}\,(\Gamma,B\vdash C)_{\text{given }p^{:\Gamma},b^{:B}}\\ - & \quad\quad\text{where}\quad b^{:B}\triangleq q\big(\text{Proof}\,(\Gamma,A\Rightarrow B\vdash A)_{\text{given }p^{:\Gamma},q^{:A\rightarrow B}}\big)\quad. +{\color{greenunder}\text{formula \textsf{``}use function\textsf{''}}:}\quad & \left(\alpha\wedge\left(\alpha\Rightarrow\beta\right)\right)\Rightarrow\beta\\ +{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & =\gunderline{\neg}(\alpha\,\gunderline{\wedge}\,(\neg\alpha\,\gunderline{\vee}\,\beta))\vee\beta\\ +{\color{greenunder}\text{de Morgan\textsf{'}s laws}:}\quad & =\gunderline{\neg\alpha\vee(\alpha\wedge\neg\beta)}\vee\beta \end{align*} -In the proof tree shown above, we obtain a proof of $S_{1}$ by applying -this proof transformer to the proofs of $S_{2}$ and $S_{3}$: \begin{align*} - & \text{Proof}\,(S_{1})_{\text{given }q^{:C}}=\text{Proof}\,(S_{3})_{\text{given }b^{:B}}\text{ where }b^{:B}\triangleq q(\text{Proof}\,(S_{2}))_{\text{given }q^{:C}}\\ - & \quad=b\text{ where }b^{:B}\triangleq q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}=q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}\quad. +{\color{greenunder}\text{identity }p\vee(\neg p\wedge q)=p\vee q\text{ with }p=\neg\alpha\text{ and }q=\beta:}\quad & =\neg\alpha\vee\gunderline{\neg\beta\vee\beta}\\ +{\color{greenunder}\text{axiom \textsf{``}use arg\textsf{''}}:}\quad & =True\quad. \end{align*} -Substituting this proof into the proof tree, we shorten the tree to: +\begin{align*} +{\color{greenunder}\text{formula \textsf{``}use Either\textsf{''}}:}\quad & \left(\left(\alpha\vee\beta\right)\wedge\left(\alpha\Rightarrow\gamma\right)\wedge\left(\beta\Rightarrow\gamma\right)\right)\Rightarrow\gamma\\ +{\color{greenunder}\text{use Eq.~(\ref{eq:ch-definition-of-implication-in-Boolean-logic})}:}\quad & =\neg\left(\left(\alpha\vee\beta\right)\wedge\left(\neg\alpha\vee\gamma\right)\wedge\left(\neg\beta\vee\gamma\right)\right)\vee\gamma\\ +{\color{greenunder}\text{de Morgan\textsf{'}s laws}:}\quad & =\left(\neg\alpha\wedge\neg\beta\right)\vee\gunderline{\left(\alpha\wedge\neg\gamma\right)}\vee\gunderline{\left(\beta\wedge\neg\gamma\right)}\vee\gamma\\ +{\color{greenunder}\text{identity }p\vee(\neg p\wedge q)=p\vee q:}\quad & =\gunderline{\left(\neg\alpha\wedge\neg\beta\right)\vee\alpha}\vee\beta\vee\gamma\\ +{\color{greenunder}\text{identity }p\vee(\neg p\wedge q)=p\vee q:}\quad & =\gunderline{\neg\alpha\vee\alpha}\vee\beta\vee\gamma\\ +{\color{greenunder}\text{axiom \textsf{``}use arg\textsf{''}}:}\quad & =True\quad. +\end{align*} +Since each proof rule of the constructive logic is translated into +a true formula in Boolean logic, it follows that a proof tree in the +constructive logic will be translated into a tree of Boolean formulas +that have value $True$ for each axiom or proof rule. The result is +that any constructive proof for a sequent such as $\emptyset\vdash f(\alpha,\beta,\gamma)$ +is translated into a chain of Boolean implications that look like +this: \[ -\xymatrix{\xyScaleY{1.5pc}\xyScaleX{3.5pc}\ar[r]\sp(0.35){S_{0}} & (\text{Right}\Rightarrow)\ar[r]\sp(0.65){q(x^{:A}\rightarrow x)_{\text{given }q^{:C}}} & \square} +True=(...)\Rightarrow(...)\Rightarrow...\Rightarrow f(\alpha,\beta,\gamma)\quad. \] +Since $\left(True\Rightarrow\alpha\right)=\alpha$, this chain proves +the Boolean formula $f(\alpha,\beta,\gamma)$. -It remains to obtain the proof of $S_{0}$ by applying the proof transformer -of the rule \textsf{``}($\text{Right}\Rightarrow$)\textsf{''}: +For example, the proof tree shown in Figure~\ref{fig:Proof-of-the-sequent-example-2} +is translated into: \begin{align*} - & \text{Proof}\,(S_{0})=\text{Proof}\,(\emptyset\vdash(\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta)\\ - & =q^{:(A\rightarrow A)\rightarrow B}\rightarrow\text{Proof}\,(S_{1})_{\text{given }q^{:C}}=q^{:(A\rightarrow A)\rightarrow B}\rightarrow q(x^{:A}\rightarrow x)\quad. +{\color{greenunder}\text{axiom \textsf{``}use arg\textsf{''}}:}\quad & True=\left(\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\neg\alpha\vee\alpha\right)\\ +{\color{greenunder}\text{rule \textsf{``}create function\textsf{''}}:}\quad & \quad\Rightarrow\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\left(\alpha\Rightarrow\alpha\right)\quad.\\ +{\color{greenunder}\text{axiom \textsf{``}use arg\textsf{''}}:}\quad & True=\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\quad.\\ +{\color{greenunder}\text{rule \textsf{``}use function\textsf{''}}:}\quad & True\Rightarrow\left(\neg\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\vee\beta\right)\\ +{\color{greenunder}\text{rule \textsf{``}create function\textsf{''}}:}\quad & \quad\Rightarrow\left(\left(\left(\alpha\Rightarrow\alpha\right)\Rightarrow\beta\right)\Rightarrow\beta\right)\quad. \end{align*} -The proof tree is now shortened to a single code expression, $q^{:(A\rightarrow A)\rightarrow B}\rightarrow q(x^{:A}\rightarrow x)$, -that serves as an evidence of proof for $S_{0}$ because this code -has type $\left(\left(A\rightarrow A\right)\rightarrow B\right)\rightarrow B$. -In this way, we have derived the code of a fully parametric function -from its type signature. - -Figure~\ref{fig:proof-transformers-for-LJ-rules} shows the proof -transformers for all the rules of the LJ algorithm. Apart from the -special rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''}, all other rules have -proof transformers using just one of the code constructions (\textsf{``}create -function\textsf{''}, \textsf{``}create tuple\textsf{''}, \textsf{``}use tuple\textsf{''}, etc.) allowed within -fully parametric code. -\subsubsection*{The LJT algorithm} +It is easier to check Boolean truth tables than to find a proof tree +in constructive logic (or to establish that no proof tree exists). +If we find that a formula is \emph{not} true in Boolean logic, we +know it is also not true in constructive logic. This gives us a quick +way of proving that some type signatures are \emph{not} implementable +as fully parametric functions. However, if a formula is true in Boolean +logic, it does not follow that the formula is also true in the constructive +logic. -As we have seen, the LJ algorithm enters a loop if the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''} -gives a sequent we already had at a previous step. That rule requires -us to prove two new sequents: +In addition to formulas shown in Table~\ref{tab:Logical-formulas-not-Boolean-theorems} +(Section~\ref{subsec:ch-Motivation-and-first-examples}), here are +three more examples of formulas that are \emph{not} true in Boolean +logic: \[ -\frac{\Gamma,A\Rightarrow B\vdash A\quad\quad\Gamma,B\vdash C}{\Gamma,A\Rightarrow B\vdash C}~(\text{Left}\Rightarrow)\quad. +\forall\alpha.\,\alpha\quad,\quad\quad\forall(\alpha,\beta).\,\alpha\Rightarrow\beta\quad,\quad\quad\forall(\alpha,\beta).\,(\alpha\Rightarrow\beta)\Rightarrow\beta\quad. \] -A sign of trouble is that the first of these sequents ($\Gamma,A\Rightarrow B\vdash A$) -does not have a simpler form than the initial sequent ($\Gamma,A\Rightarrow B\vdash C$). -So, it is not clear that we are getting closer to completing the proof. -If $A=C$, the new sequent will simply repeat the initial sequent, -immediately creating a loop. +These formulas are also \emph{not} true in the constructive logic. -In some cases, a repeated sequent will occur after more than one step. -It is not easy to formulate rigorous conditions for stopping the loop -or for avoiding the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''}. +\subsection{The constructive logic and the law of excluded middle} -The LJT algorithm solves this problem by removing the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''} -from the LJ algorithm. Instead, \emph{four} new rules are introduced. -Each of these rules contains a different pattern instead of $A$ in -the premise $A\Rightarrow C$: -\begin{align*} -\text{(}A\text{ is atomic)\,}\frac{\Gamma,A,B\vdash D}{\Gamma,A,{\color{blue}A\Rightarrow B}\vdash D}~(\text{Left}\Rightarrow_{A})\qquad & \qquad\frac{\Gamma,A\Rightarrow B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\wedge B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\wedge})\\ -\frac{\Gamma,B\Rightarrow C\vdash A\Rightarrow B\quad\quad\Gamma,C\vdash D}{\Gamma,{\color{blue}(A\Rightarrow B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\Rightarrow})\qquad & \qquad\frac{\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\vee B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\vee}) -\end{align*} -The rule \textsf{``}$\text{Left}\Rightarrow_{A}$\textsf{''} applies only if the implication -starts with an \textsf{``}atomic\textsf{''} type expression, i.e., a single type parameter -or a unit type. In all other cases, the implication must start with -a conjunction, a disjunction, or an implication, which means that -one of the three remaining rules will apply. +Computations in the Boolean logic are often performed using truth +tables. It is perhaps surprising that the proof rules of the constructive +logic are \emph{not} equivalent to checking whether some propositions +are $True$ or $False$ via a truth table. A general form of this +statement was proved by K.~G\"odel\index{Kurt@Kurt G\"odel} in +1932.\footnote{See \texttt{\href{https://plato.stanford.edu/entries/intuitionistic-logic-development/\#SomeEarlResu}{plato.stanford.edu/entries/intuitionistic-logic-development/}}} +In this sense, constructive logic does \emph{not} imply that every +proposition is either $True$ or $False$. This is not intuitive and +requires getting used to. Reasoning in the constructive logic must +use the axioms and derivation rules directly, instead of truth tables. -The LJT algorithm retains all the rules in Figure~\ref{fig:proof-transformers-for-LJ-rules} -except the rule \textsf{``}($\text{Left}\Rightarrow$)\textsf{''}, which is replaced -by the four new rules. It is far from obvious that the new rules are -equivalent to the old ones. It took mathematicians several decades -to come up with the LJT rules and to prove their validity. This book -will rely on that result and will not attempt to prove it. +The reason Boolean logic can use truth tables is that every Boolean +proposition is either $True$ or $False$. This can be written as +the formula $\forall\alpha.\,(\neg\alpha\vee\alpha=True)$. Table~\ref{tab:Proof-rules-of-constructive-and-boolean} +uses the Boolean identity $\left(\alpha\Rightarrow\beta\right)=(\neg\alpha\vee\beta)$, +which does not hold in the constructive logic, to translate the constructive +axiom \textsf{``}$\text{use arg}$\textsf{''} into the Boolean axiom $\neg\alpha\vee\alpha=True$. +The formula $\forall\alpha.\,\neg\alpha\vee\alpha=True$ is known +as the \textbf{law of excluded middle}.\footnote{See \texttt{\href{https://en.wikipedia.org/wiki/Law_of_excluded_middle}{https://en.wikipedia.org/wiki/Law\_of\_excluded\_middle}}}\index{law of excluded middle} +It is remarkable that the constructive logic \emph{does not have} +the law of excluded middle. It is neither an axiom nor a derived theorem +in constructive logic. -The proof transformers for the new rules are shown in Figure~\ref{fig:proof-transformers-for-LJT-rules}. -Figures~\ref{fig:proof-transformers-for-LJ-rules}\textendash \ref{fig:proof-transformers-for-LJT-rules} -define the set of proof transformers sufficient for using the LJT -algorithm in practice. The \index{curryhoward library@\texttt{curryhoward} library}\lstinline!curryhoward! -library\texttt{}\footnote{See \texttt{\href{https://github.com/Chymyst/curryhoward}{https://github.com/Chymyst/curryhoward}}} -implements these proof transformers. +To see why, translate the constructive logic formula $\forall\alpha.\,\neg\alpha\vee\alpha=True$ +into a type. The negation operation ($\neg\alpha$) is defined as +the implication $\alpha\Rightarrow False$. So, the formula $\forall\alpha.\,\neg\alpha\vee\alpha$ +corresponds to the type $\forall A.\,\left(A\rightarrow\bbnum 0\right)+A$. +Can we compute a value of this type via fully parametric code? For +that, we need to compute either a value of type $A\rightarrow\bbnum 0$ +or a value of type $A$. This decision needs to be made in advance +independently of $A$, because the code of a fully parametric function +must operate in the same way for all types. As we have seen in Example~\ref{subsec:ch-Example-type-identity-A-0}, +a value of type $A\rightarrow\bbnum 0$ exists if the type $A$ is +itself $\bbnum 0$. But we do not know in advance whether $A=\bbnum 0$. +Since there are no values of type $\bbnum 0$, and the type parameter +$A$ could be, say, \lstinline!Int!, we cannot compute a value of +type $A\rightarrow\bbnum 0$. -The most complicated of the new rules is the rule \textsf{``}($\text{Left}\Rightarrow_{\Rightarrow}$)\textsf{''}. -Applying that rule\textsf{'}s transformer results in evidence-of-proof code -that is longer than the code obtained via LJ\textsf{'}s rule transformers. -The resulting code will need to be simplified symbolically. +Why is it impossible to implement a value of the type $\left(A\rightarrow\bbnum 0\right)+A$? +Surely, the type $A$ is either void or not void. If $A$ is void +then $\left(A\rightarrow\bbnum 0\right)\cong\bbnum 1$ is not void +(as Example~\ref{subsec:ch-Example-type-identity-A-0} shows). So, +one of the types in the disjunction $\left(A\rightarrow\bbnum 0\right)+A$ +should be non-void and have values that we can compute. -As an example of using the LJT algorithm, we prove the sequent $S_{0}=\emptyset\vdash((\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta$ -from the previous section. At each step, only one LJT rule applies -to each sequent. The initial part of the proof tree looks like this:\vspace{-1\baselineskip} -\[ -\xymatrix{\xyScaleY{1.5pc}\xyScaleX{6pc} & & & ~\\ -\ar[r]\sp(0.4){\emptyset\vdash((\alpha\Rightarrow\alpha)\Rightarrow\beta)\Rightarrow\beta} & (\text{Right}\Rightarrow)\ar[r]\sp(0.5){(\alpha\Rightarrow\alpha)\Rightarrow\beta\vdash\beta} & (\text{Left}\Rightarrow_{\Rightarrow})\ar[ru]\sp(0.65){\beta\vdash\beta}\ar[r]\sp(0.65){\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha} & ~ -} -\] -The proofs for the sequents $\beta\vdash\beta$ and $\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha$ -are the same as before: -\[ -\text{Proof}\,(\beta\vdash\beta)_{\text{given }y^{:B}}=y\quad,\quad\quad\text{Proof}\,(\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha)_{\text{given }r^{:A\rightarrow B}}=x^{:A}\rightarrow x\quad. -\] -Substituting these proofs into the proof transformer of the rule \textsf{``}($\text{Left}\Rightarrow_{\Rightarrow}$)\textsf{''} -produces this code: -\begin{align*} - & \text{Proof}\,((\alpha\Rightarrow\alpha)\Rightarrow\beta\vdash\beta)_{\text{given }q^{:(A\rightarrow A)\rightarrow B}}=q\big(\text{Proof}\,(\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha)_{\text{given }r^{:A\rightarrow B}}\big)\\ - & \quad\quad\text{where }r^{:A\rightarrow B}=a^{:A}\rightarrow q(\_^{:A}\rightarrow a)\\ - & =q(x^{:A}\rightarrow x)\quad. -\end{align*} -The proof of $\alpha\Rightarrow\beta\vdash\alpha\Rightarrow\alpha$ -does not actually use the intermediate value $r^{:A\rightarrow B}$ -provided by the proof transformer. As a symbolic simplification step, -we may simply omit the code of $r$. The \lstinline!curryhoward! -library always performs symbolic simplification after applying the -LJT algorithm. +While this argument is true, it does not help implementing a value +of type $\left(A\rightarrow\bbnum 0\right)+A$ via fully parametric +code. It is not enough to know that one of the two values \textsf{``}should +exist\textsf{''}. We need to know \emph{which} of the two values exists, and +we need to write code that computes that value. That code may not +decide what to do depending on whether the type $A$ is void, because +the code must work in the same way for all types $A$ (void or not). +As we have seen, that code is impossible to write. + +In Boolean logic, one may prove that a value \textsf{``}should exist\textsf{''} by +showing that the non-existence of a value is contradictory in some +way. However, any practically useful program needs to \textsf{``}construct\textsf{''} +(i.e., to compute) actual values. The \textsf{``}constructive\index{constructive logic}\textsf{''} +logic got its name from this requirement. So, it is the constructive +logic (not the Boolean logic) that provides correct reasoning about +the types of values computable by fully parametric functional programs. + +If we drop the requirement of full parametricity, we \emph{could} +implement the law of excluded middle\index{law of excluded middle}. +Special features of Scala (reflection, type tags, and type casts) +allow programmers to compare types as values and to determine what +type was given to a type parameter when a function is applied: +\begin{lstlisting}[mathescape=true] +import scala.reflect.runtime.universe._ + // Convert the type parameter T into a special value. +def getType[T: TypeTag]: Type = weakTypeOf[T] + // Compare types A and B. +def equalTypes[A: TypeTag, B: TypeTag]: Boolean = getType[A] =:= getType[B] + + // excludedMiddle has type ${\color{dkgreen}\forall A.\left(A\rightarrow\bbnum 0\right)+A}$. +def excludedMiddle[A: TypeTag]: Either[A, A => Nothing] = + if (equalTypes[A, Nothing]) Right((identity _).asInstanceOf[A => Nothing]) // Return ${\color{dkgreen}\text{id}:\bbnum 0\rightarrow\bbnum 0}$. + else if (equalTypes[A, Int]) Left(123.asInstanceOf[A]) // Produce some value of type Int. + else if (equalTypes[A, Boolean]) Left(true.asInstanceOf[A]) // Produce some value of type Boolean. + else ??? // Write more definitions to support all other Scala types. + +scala> excludedMiddle[Int] +res0: Either[Int,Int => Nothing] = Left(123) -\begin{figure} -\begin{centering} -\noindent\fbox{\begin{minipage}[t]{1\columnwidth - 2\fboxsep - 2\fboxrule}% -\begin{align*} -\frac{\Gamma,A,B\vdash D}{\Gamma,A,{\color{blue}A\Rightarrow B}\vdash D}~(\text{Left}\Rightarrow_{A})\quad & \quad\text{Proof}\,(\Gamma,A,A\Rightarrow B\vdash D)_{\text{given }p^{:\Gamma},x^{:A},q^{:A\rightarrow B}}\\ - & \quad\quad=\text{Proof}\,(\Gamma,A,B\vdash D)_{\text{given }p,x,q(x)}\\ -\frac{\Gamma,A\Rightarrow B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\wedge B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\wedge})\quad & \quad\text{Proof}\,(\Gamma,(A\wedge B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:A\times B\rightarrow C}}\\ - & \quad\quad=\text{Proof}\,(\Gamma,A\Rightarrow B\Rightarrow C\vdash D)_{\text{given }p,(a^{:A}\rightarrow b^{:B}\rightarrow q(a\times b))}\\ -\frac{\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D}{\Gamma,{\color{blue}(A\vee B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\vee})\quad & \quad\text{Proof}\,(\Gamma,(A\vee B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:A+B\rightarrow C}}\\ - & \quad\quad=\text{Proof}\,(\Gamma,A\Rightarrow C,B\Rightarrow C\vdash D)_{\text{given }p,r,s}\\ - & \quad\quad\text{where}~r\triangleq a^{:A}\rightarrow q(a+\bbnum 0)\text{ and }s\triangleq b^{:B}\rightarrow q(\bbnum 0+b)\\ -\frac{\Gamma,B\Rightarrow C\vdash A\Rightarrow B\quad\quad\Gamma,C\vdash D}{\Gamma,{\color{blue}(A\Rightarrow B)\Rightarrow C}\vdash D}~(\text{Left}\Rightarrow_{\Rightarrow})\quad & \quad\text{Proof}\,(\Gamma,(A\Rightarrow B)\Rightarrow C\vdash D)_{\text{given }p^{:\Gamma},q^{:\left(A\rightarrow B\right)\rightarrow C}}\\ - & \quad\quad=\text{Proof}\,(\Gamma,C\vdash D)_{\text{given }p,c}\\ - & \quad\quad\text{ where}~c^{:C}\triangleq q\big(\text{Proof}\,(\Gamma,B\Rightarrow C\vdash A\Rightarrow B)_{\text{given }p,r}\big)\\ - & \quad\quad\text{ and }r^{:B\rightarrow C}\triangleq b^{:B}\rightarrow q(\_^{:A}\rightarrow b) -\end{align*} -% -\end{minipage}} -\par\end{centering} -\caption{\label{fig:proof-transformers-for-LJT-rules}Proof transformers for -the four new rules of the \index{LJT algorithm|textit}LJT algorithm.} -\end{figure} +scala> excludedMiddle[Nothing] +res1: Either[Nothing,Nothing => Nothing] = Right() +\end{lstlisting} +In this code, we check whether $A=\bbnum 0$. If so, we can implement +$A\rightarrow\bbnum 0$ as an identity function of type $\bbnum 0\rightarrow\bbnum 0$. +Otherwise, we know that $A$ is one of the existing Scala types (\lstinline!Int!, +\lstinline!Boolean!, etc.), which are not void and have values that +we can simply write down one by one in the subsequent code. -The reason the LJT algorithm terminates is that each rule replaces -a given sequent by one or more sequents with simpler premises or goals.\footnote{The paper \texttt{\href{http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.35.2618}{http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.35.2618}} -shows that the LJT algorithm terminates by giving an explicit decreasing -measure on proof trees.} This guarantees that the proof search will terminate either with -a complete proof or with a sequent to which no more rules apply. An -example of such a \textsf{``}dead-end\textsf{''} sequent is $\alpha\vdash\beta$ where -$\alpha$ and $\beta$ are different, unrelated propositions. In that -situation, the LJT algorithm concludes that the initial sequent cannot -be proved. +Explicit\index{type casts} \textbf{type casts}, such as \lstinline!123.asInstanceOf[A]!, +are needed because the Scala compiler cannot know that \lstinline!A! +is \lstinline!Int! in the scope where we return \lstinline!Left(123)!. +Without a type cast, the compiler will not accept \lstinline!123! +as a value of type \lstinline!A! in that scope. + +The method \lstinline!asInstanceOf! is dangerous because the code +\lstinline!x.asInstanceOf[T]! disables the type checking for the +value \lstinline!x!. This tells the Scala compiler to believe that +\lstinline!x! has type \lstinline!T! even when the type \lstinline!T! +is inconsistent with the actually given code of \lstinline!x!. The +resulting programs compile but may give unexpected results or crash. +These errors would have been prevented if we did not disable the type +checking. In this book, we will avoid writing such code whenever possible.% +\begin{comment} +showing here this is equivalent in Scala just different syntax importantly +non theorems cannot be implemented in code some on theorems are statements +in logic that cannot be derived statements that are false or undereye +verbal examples of these statements are these for all a from one follows +a now this is certainly suspicious in terms of logic what if a were +false then we would have it from true false false that\textsf{'}s very obviously +wrong and we cannot implement a function of this type to implement +it we would have to take a unit argument and produce a value of type +a where a is arbitrary type but how can we produce a value of type +a of the type that we don't even know what it is and there is no data +for us to produce that value so it is impossible another example of +an impossible type is this type so from a plus B follows a if you +wanted to implement this function you would have to take a value of +disjunction type a plus B and return a value of type a but how can +you do that what exodus Junction type happens to contain B and no +a just B it cannot contain a if it contains a B it\textsf{'}s a disjunction +so then we don't have an A and then we again cannot produce any and +having a B which is a completely different arbitrary type doesn't +help us to produce me exactly the same reason shows why we cannot +produce an A a and B given a because that requires a B we cannot produce +and also this is not implementable because we are required to produce +an A but all we have is a function from A to B this function will +consume an A if given only this function cannot possibly produce an +A for us but we are required to produce an A as a result so we cannot +and also there is no proof of this formula in the logic so these examples +actually lead us to a natural question how can we decide given a certain +formula whether it is a theorem in logic and therefore whether it +can be implemented in code it is not obvious consider this example +can we write a function with this type in Scala it is not obvious +can we prove this formula it is not clear not quite obvious right +now suppose I were of the opinion that this cannot be proved but how +do I show that this cannot be proved I certainly cannot just try all +possible proofs that would be infinitely many possible proofs that +would give me all kinds of other formulas and that would give me nothing +that I can stand oh how to answer these questions so it is really +a very hard question we are not going to try to answer it on our own +we were going to use the results of mathematicians they have studied +these questions for many many years for centuries logic has been studied +since ancient Greece more than 2,000 years of study all we need to +do is to find out by what name mathematicians call this logic they +are probably already studied it what kind of logic is this that we +are using that follows from the type constructions remember and the +very beginning of our consideration we started with the type constructions +that our programming languages have so that\textsf{'}s set of type constructions +specifies the set of rules of derivation of the logic mathematicians +call this logic intuitionistic propositional logic or IPL also they +call it constructive propositional logic but it is less frequently +used most frequently used name is this and mathematicians also call +this a non classical logic because this logic is actually different +from the boolean logic that we are familiar with the logic of the +values true and false and their truth tables I assume that you are +familiar with those computations using truth tables and operations +and or not in the boolean logic so actually this logic the logic of +types as I call it or intuitionistic propositional logic is very different +from boolean logic in certain ways it\textsf{'}s similar in other ways disjunction +for instance works very differently here\textsf{'}s an example consider this +sequence if it has given that from a follows B plus C then either +from a follows B or from a follows C it sounds right from the common-sense +point of it if if B plus C Falls a B or C if I was I'm using plus +as a logical or so if B or C follows then it kind of makes sense either +B follows or C Falls indeed this is correct in the boolean logic which +we can find out by writing the truth table so we enumerate all the +possibilities for a B and C to be true or false or eight such possibilities +and for each of those possibilities we write the truth value of this +the truth value of this and we see from the table that whenever this +is true then this is also true in the boolean logic but this does +not hold in the intuitionistic logic for the logic of types well why +does it not hold that\textsf{'}s counterintuitive well in fact there is very +little that\textsf{'}s intuitive about this so-called intuitionistic logic +actually we need to think differently about this logic we need to +think can we implement an expression of this sequent so implementing +it would mean if we're given this expression we can build an expression +of this type so we're given an expression of type A to B plus C let\textsf{'}s +say some F of this type can we build an expression of this type we +can this differently by asking can we implement a function that takes +this as an argument and returns this well we know that this is equivalent +one of our derivation rules is that if you have this sequence then +you can also have a sequence that is a function type from this to +this so for the programmer it is easier to reason about a function +taking this as an argument and returning this so how can we implement +this function this function takes F and needs to return a value of +this type so the body of this function if we could implement it and +have to construct a value of type either of something there are only +two ways of constructing a value of type either one is to construct +the left value second is to construct the right value how do we decide +whether to construct the left value or the right value we have to +decide it somehow on the basis of what information can we decide it +we don't actually have any such information what we have here is a +function from a to either BC so given some value of a of type a we +could compute f of that value and then we would have either B or C +we could decide them whether to we could take them that B or that +C but that\textsf{'}s not what we need to return we don't need to return either +of BC we need to return either of this function or that function and +that function is not yet applied to any a it is it is too late for +us to ask what is the a we already have to return the left of this +or a right of that in other words this type either of something-something +is not itself a function of a it contains functions away but itself +it cannot be decided on the basis of any assets too late so we need +to supply a left or right so here right away immediately we have to +decide whether this will return a left or a right and we cannot really +decide that if we decide we return the left we must then return a +function from A to B so there\textsf{'}s no way for us to construct this function +if we're given this function because this function could sometimes +return C instead of B and then we'll be stuck we cannot do this and +we can also return we cannot also return the right either so it is +impossible to implement a function of this type implication also works +a little differently in the intuitionistic logic here\textsf{'}s an example +this holds in boolean logic but not in intuitionistic logic again +let\textsf{'}s see why how can we compute this given this this function will +give us an e only when given an argument of this type but how can +we produce a value of this type we cannot we don't have information +that will allow us to produce a value of this type a and B are some +arbitrary types remember there is universal quantifier outside of +all this for all a and for all B we're supposed to produce this and +that is impossible we don't have enough data to produce some values +type a and so we cannot implement this function conjunction works +kind of the same as in boolean logic so here\textsf{'}s an example this implemented +and this is also in boolean logic a true theorem now in boolean logic +the usual way of deciding whether something is true or something is +a theorem is to write a truth table unfortunately the intuitionistic +logic cannot have a truth table it cannot have a fixed number of truth +values even if you allow more than two truth values such that the +validity of formulas the truth of theorems can be decided on the basis +of the truth table this was shown by noodle and this means we should +not actually try to reason about this logic using truth values it +is not very useful even an infinite infinite number of truth values +will not help instead however it turns out that this logic has a decision +procedure or an algorithm and this algorithm is guaranteed either +to find the proof for any given formula of the internation intuitionistic +logic or to determine that there is no proof for that formula the +algorithm can also find several in equivalent proofs if there is a +theorem so a theorem could have several in equivalent proofs and since +each proof could be automatically translated into code of that type +it means we could generate several in equivalent expressions of some +type sometimes so that is the situation with this logic which we discover +if we write if we read papers about intuitionistic propositional logic +that are available in the literature and their open source projects +on the web such as the gen GHC which is a compiler plugin for haskell +this is another project doing the same thing and for Scala are implemented +occurred the Clary Howard library both of these Scala and Haskell +all of these color and Haskell projects do the same thing they take +a type of some expression for function and generate code for it automatic +by translating the type into sequence finding a proof in this logic +using the algorithm and translating that proof back into code in the +way that we have seen in an example it is interesting that all these +provers and there\textsf{'}s a few others there\textsf{'}s one more for the idris language +I did not mention here they all used the same decision procedure or +the same basic algorithm which is called ljt which was explained in +a paper by dick off here they all side the same paper and I believe +this is so because most other papers on this subject are unreadable +to non-specialists they are written in a very complicated way or they +describe algorithms that are too complicated so I will show how this +works in the rest of this tutorial in order to find out how to get +an algorithm we need to ask well first of all do we have the rules +of derivation that allow us to create an algorithm already here is +a summary of the axioms and the rules of derivation that we have found +so far these are direct translations of the cold expressions that +we held in the programming language in the notation of sequence now +there\textsf{'}s one other notation for derivation rules which looks like a +fraction like this the numerator is one or more sequins and the denominator +is a sequence and this notation means in order to derive what is in +the denominator you have to present proofs for what is in the numerator +so this is the convention in the literature this fraction like syntax +or notation now we keep in mind that proofs of sequence are actually +just called expressions that have these types as some variables and +this type is the entire expression so these are directly responding +to proofs of this sequence and to the proofs of these derivation rules +and so if we have a proof that operates by combining some of these +axioms and some of these generation rules which directly translate +that back into code now the question is do these rules give us an +algorithm for finding a proof the answer is no how can we use these +rules to obtain an algorithm well suppose we need to prove some sequence +like this in order to prove it we could first see if the sequence +is one of the axioms if so then we have already proved if we know +what expression to write now in this case none of the axioms match +this so much means maybe a is a times B so B here is C and then on +the Left we must have C or you must have a times B now we don't you +don't have C on the left as we have because even that\textsf{'}s not the same +we also don't have a times B at the premise we have a but we don't +have a times B so these rules don't match the other rules don't match +the premises and the goal either but also these rules so how can we +use them well when the writer must be an implication we don't have +an application on the right here we could try to delete some of the +premises because it\textsf{'}s unused well actually it doesn't look like a +good idea could you read a for example and we end up with an really +hopeless sequence from B plus C we cannot get an A ever and so but +sounds hopeless so this doesn't seem to help and changing the order +doesn't seem to help much either and so we cannot find matching rules +but actually this sequence is provable just a clever combination of +what axiom to start with and what role to use and then again some +axiom and so on it will give us that time sure because I know how +to write code for this this is not difficult you have a function with +two arguments one of them is a the other is B plus C so disjunction +of either B C and we are supposed to produce a disjunction of tuple +a B or C that\textsf{'}s easy look at this disjunction if we have a B in this +disjunction then we can produce a left of the tuple a B because we +always have an A anyway if we have a see in this disjunction then +we could return this part of the disjunction in the right of C and +we're done but unfortunately we see that the rules here do not give +us an algorithm for deciding this we need a better formulation of +the logic again mathematicians need to save us from the situation +and they have done so mathematicians have studied this logic for a +long time starting from the early 20th of the last century the first +algorithmic formulation of the logic that was found is due to Jensen +who published what he called the calculus just ignore the word calculus +it means not very much complete and sound calculus means that he came +up with some rules of derivation which are summarized here such that +they are equivalent to these they derive all the same theorems and +only the same theorems so they derive all the stuff that is right +and only that stuff they don't derive any wrong statements it\textsf{'}s very +hard to come up with such a system of axioms and derivation rules +that are equivalent to another one in this sense also it\textsf{'}s very hard +to prove that these are actually the rules that will give you all +the theorems that could be right in this logic that you can actually +derive all the theorems that are right yet work is already done by +mathematicians so we're not going to try to do it ourselves we're +just going to understand how these rules work now the syntax here +is slightly enhanced compared with this the enhancement is that their +names pretty cool now these are just labels they don't really do anything +in terms of sequence these help us identify which we all have has +been applied to which sequence and that\textsf{'}s all we do so other than +that it is the same notation so the fraction such as this one means +that there is a sequence in the denominator which we will prove if +there are proofs given for sequence in the numerator in this rule +there are two sequence of them in the numerator other rules may have +one sequence in the numerator or no sequence in the numerator so these +rules that will have no previous sequence required those are axioms +this axiom means if you have an atomic X in other words it\textsf{'}s a variable +it\textsf{'}s a type variables not not a complicated expression just attack +variable and you can derive that same variable this is our accion +right here now why is it important that this is atomic that this is +type variable and not a more complicated expression actually not important +but it\textsf{'}s the simplest rule that you can come up with and mathematicians +always like the most minimal set of rules so that\textsf{'}s why they say let\textsf{'}s +only consider this rule for the type variables X not for more complicated +expressions but we can consider this rule for any expression of course +the identity axiom well here is a truth truth axiom net which derives +the truth which is the ste symbol which I denote it by one the format +in logical notation this is the T symbol well let\textsf{'}s just call this +one for clarity so that can be derived from any premises with no previous +sequence necessary none of these other rules now what do these other +rules do they do an interesting thing actually each of these rules +is either about something in the sequence on the left to the trans +time or something in the sequence to the right of the transplant which +I here shown in blue so these are the interesting parts of the sequence +that are being worked on or transformed by the rule so here\textsf{'}s an example +this rule is actually two rules the eyes the index so I is one or +two another two rules just written for gravity like this with index +I and each of them says you will prove this if you prove one of if +you prove this so for example you will prove C given if you're given +a a one A two if you will prove C given just a one which makes sense +because if you can prove C given a one you don't need a two we can +ignore this a T we can already proved C from anyone so in this way +it would be proved and so all these rules work in this way you can +prove what\textsf{'}s on the bottom of the seat of the of the fraction if you're +given proofs for what\textsf{'}s on the top so these are eight derivation rules +and two axioms we can use this now to make a proof search how do we +do that I start with a sequence we see which rule matches that sequence +so the sequence must have something on the left and something on the +right well at least one of these it cannot be empty so it must be +something somewhere and there are only four kinds of expressions in +our logic type variables conjunctions implications and disjunctions +now notice I'm using this arithmetic arithmetic all notation for logic +just because I like it better and I will show that it has advantages +later so we take a sequence we see which rule matches one of them +won't match because either in the premise we have one of these expressions +were in the goal we have one of these expressions and then we find +the rule of match that matches we apply that rule so we now have new +sequence one or more that we will need to be proved and if they're +true then we fork the tree and now we have to prove both of them son-in +we continue doing that for each of the sequence until we hit axioms +so the tree will and this leaf or we hit a sequence to which no rule +applies in which case we cannot prove it and the entire thing is unprovable +so in the search tree there will be sequence at the nodes of the tree +and proofs will be at the edges of the tree so each node sends its +proof to the root of the tree this calculus is guaranteed by mathematicians +to be such that indeed if you cannot find a rule that applies that +means the sequence cannot be proved which was not the case here the +sequence can be proved and yet we cannot find a rule that applies +so in this calculus we can use bottom-up approach to make a proof +search as a tree here we cannot that is the advantage capitalizing +on the mathematicians results let us look at an example suppose we +want to prove this formula this theorem so first step we need to write +a sequence and this needs to be proved from no premises so we write +a sequence s0 which has an empty set of premises this is a single +now what rule applies to this sequence with your bottom up so in other +words we look at these rules and they refine which denominator matches +our sequential and our cylinders empty set on the left so all the +rules on the left cannot be applied but on the right we have an expression +which is an implication at the top level of this expression there +is this implies that so this is of the form a implies B so this rule +applies we have a sequence of the form something in our case this +is an empty set and then a implies B so we apply this rule which is +the right implication and we get a new sequence which is that what +was here before the implication is now put on the left to the trans +of the to the left of the trans time and it means that this expression +needs to be now to the left of the turnstile so now this is the sequence +s1 now we need to prove s1 well we see what rule applies to us one +well on the right there is just Q so nothing can be done of these +rules and Q is not truth so we cannot use the axiom either so let\textsf{'}s +look at their left rules on the Left we have now an implication so +this is let\textsf{'}s say a and this is B so we have a rule which has a implication +B on the left this is the row left implication let\textsf{'}s apply it that +law will give us two new sequence so these two new sequence are s2 +and s3 no these ones as you can check if you match a location B against +this implication Q so this is a this is B so then you get these two +sequence now we have to prove these two sequence as 2 and s 3 s 3 +is easy it is just the axiom of identity it is this now as 2 again +has an implication on the left let\textsf{'}s again apply the rule left implication +to that we get two more sequence as foreign s5 as for is this because +5 is this so now actually we are in trouble because as 2 and s 4 is +are the same sequence as 5 actually we could prove with some more +work but that won't help because we are in a situation when to prove +as two we need to prove again s 2 so that\textsf{'}s it that\textsf{'}s a loop that +will never give us anything it means we applied the wrong rule so +we need to backtrack this step when we apply the rule left implication +to s 2 we erase is 4 in this 5 and try a different rule to apply to +s 2 which rule can apply to s 2 well as to is this it actually has +implication on the right so we can use the right implication rule +and if we do that we get a sequence s 6 which is this and this sequence +immediately follows from the identity axiom because it has promise +are on the left and premise are and goal are on the right and that +is this axiom whatever other premises and the premise X on the left +premise X on the right and that is a type variable so that\textsf{'}s perfect +we have done the proof as 6 follows from the axiom and therefore we +have proved s0 no more sequins need to be proved and because sequence +s0 shows this to be derived from no premises than this formula is +the theorem that\textsf{'}s what the theorem means in the logic so that is +how we use this calculus to do proof search now we notice that we +were a bit stuck at some point we had a loop now if we are in the +loop we don't know what to do maybe we need to continue applying the +same rule maybe some new sequence come up or maybe we should not continue +it is not clear what to do and just looking at the rule left implication +shows us that it\textsf{'}s copying this premise a implication B it is copied +into the premises of the new sequence and so it will generate a loop +assuredly after the second time you apply it however this sequence +might be new so we might need to apply it second time we don't know +that so that is a problem it will do now there have been a lot of +work trying to fix this problem and literally decades from research +by mathematicians the main ones I found were what are the off we published +in the Soviet Union who de Meyer and dick Hoff who published in the +United States over this time discovered gradually a new set of rules +which is called ljt or the calculus ljt which cures this problem of +looping the way it clears this problem is by replacing this rule left +implication through four new rules which are listed here all other +rules are kept the same from this calculus except the rule left implication +which is replaced in what way so left implication was applying it +applied to a sequence when the sequin had an implication among the +premises or on the left to the left of the turnstile the new rules +look in more detail at what is that implication so that implication +could have one of the four expressions as the argument of the implication +it could have an atomic expression as the argument it would have a +conjunction as the argument could have a disjunction as the argument +or it could have an implication as the argument in our logic there +are no more expressions except these four atomic variables conjunctions +disjunction and implications and so we have here enumerated all the +possibilities for what could be to the left of the implication in +this premise which I have here shown in the blue in blue and so for +each of these we do certain things replacing this sequence with one +or more other sequence again it\textsf{'}s quite a lot of work to prove that +these rules are equivalent to these and also that the new rules are +somehow better they are not giving loops a lot of work which I am +NOT going to go through because that\textsf{'}s far too complicated for the +scope so what we need suffice it to say that we have very smart people +who published on this and it is reasonably sure that this is correct +so the T in the name lgt starts stands for terminating so if we use +these rules in the same way by by creating a proof tree the proof +tree will have no loops and will terminate after a finite number of +steps and there is actually this paper that is also helpful for understanding +how to implement this algorithm and this paper shows explicitly how +to construct an integer function from sequence to integers which is +a measure of the complexity of the sequence and this measure decreases +every time you apply a rule so it strictly decreases and since this +is a strictly decreasing measure on the proof tree it means that all +the next nodes in the proof tree will have a smaller value of this +measure so eventually it will hit zero and the proof tree will terminate +at that leaf either that or you have no more rules to apply and if +you have no more laws to apply then again mathematicians have proved +it means our sequence cannot be proved so this is an important result +that we are going to use and note that this this rule is quite complicated +it does a very interesting thing it takes this expression which has +implication inside an implication and it transforms this expression +in a weird way namely the B here is separated from the C by parenthesis +but here it is not separated so this transformation is highly non-trivial +and unexpected and its validity is based on this theorem that this +in the intuitionistic logic is equivalent to this equivalent means +they're both following from the other so from this promos that and +from there follows this so this key theorem was attributed to rob +you off my dick off in this paper and this is this lemma 2 which says +that if this sorry that the this derivation is if and only if that +derivations will have these two equivalences and the proof is trivial +and the 34 is a reference to to borrow be off now when a mathematician +says that something is trivial doesn't mean that a statement is easy +to understand it doesn't mean that the proof is easy to find or that +it has trees easy to understand it means none of these things it just +means that right now for this mathematician it is not interesting +to talk about how it is done that\textsf{'}s all it means could be for any +number of reasons for example mathematicians could just be lazy or +have no time to again explain this and so they say it\textsf{'}s trivial don't +be don't be deceived when you see somebody says that something is +trivial in a mathematical text so to prove this one stepping stone +could be to prove this first this is an easier theorem and if you +prove this then clearly from here you can get B to C B to C you can +substitute in here you can get a to B and then you have here a to +B so in this way you can show this equivalence in one direction now +the proof of this statement is obviously trivial in order to show +the expression of this type I will use my short notation so this is +F which has this type the first argument of the function the second +is B which is at this type then we need to produce a see how do we +produce a C we apply F to an argument of this type the argument of +this type is a function that takes a and returns a B so we take some +X of type a and we return a B which was this B so we ignore this X +we just returned that B and that\textsf{'}s the argument of F so this expression +is the proof of this sequence in other words this is the code that +has this type and therefore the proof must be available somehow so +the details of proving this theorem are left as an exercise for the +reader again when you see in a mathematical text that something is +left as an exercise for the reader it does not mean that it is easy +to do it does not mean that for you it would be a useful exercise +to do it also does not mean that the author knows how to do it it +means none of these things it just means the author doesn't feel like +doing it right now and showing it to you for whatever reason could +be because they are lazy it could be because I don't know how to do +it could be because they feel that they should know how to do it but +they don't really do know how to do it could be any of these reasons +don't be deceived when you see something like this but of course I +had to actually produce an expression function of this type in order +to implement my curry forward language because as I will show in a +moment we need to be able to implement all these has code in order +to help approver so why is that we believe the mathematicians that +the new rules are equivalent to the old rules which means that if +you find a proof using these rules somehow you should be able to find +the proof also using our initial rules which means that if you found +that proof it would easily translate that to code because each step +here is directly corresponding to a certain code expression as we +have seen at the beginning of this tutorial these cold expressions +from each of these operations so in order to do this with new rules +in other words in order to create code from proof using new rules +we need to show equivalence or we need to show how to get code out +of each of the new rules now proof of a sequence means that we have +some expression let\textsf{'}s say T what uses variables a B and C of these +types and expression itself has type G and also as I have shown this +could be conveniently seen as a function the T as a function from +a B and C from these three arguments to the type G so for each sequencing +a proof we should be able to show either that it follows from an axiom +one of these or that it show it follows from a derivation rule and +the derivations all transforms one proof into another the axioms are +just fixed expressions as we had before the axiom that actually didn't +change between our initial formulation of the logic and the new calculus +lgt they actually did not change the derivation rules changed each +new derivation rule means that you're given expressions that prove +the sequence in the numerator one or more and you are out of these +expressions somehow you have to construct an expression that proves +this sequence now when I say an expression proves the sequence what +it means is that expression has the type that is described by the +sequence it\textsf{'}s the same thing because we described types of expressions +through sequence and only those sequence that correspond to valid +and existing expressions in the programming language only those sequence +can be proved by the logic this is by construction so now we need +to just find what are these expressions that corresponds to each of +the derivation rules in each rule has a proof transformer function +as I call it and the proof transfer function is explicitly a function +that takes one or more expressions that are in the numerator and converts +that to the expression in the denominator that has this type so it +has an expression as it has an explicit function we need to write +down for each of the derivation rules so let\textsf{'}s see how this is done +for these two examples of derivation laws first example have a rule +that says if you want to derive this sequence we need to derive these +two sequence now this sequence represents an expression of type C +which uses an expression of type A plus B so let\textsf{'}s represent this +as a function from a plus B to C now we will be able to just ignore +these other premises which are common arguments and all these functions +we just pass them and we don't write them out what is the proof transformer +for this derivation rule the proof transformer for it is a function +that has two arguments t1 which is the proof of this must be a function +of type II to see and t2 which is a proof of this sequence which must +be a function of type B to see now earlier I said that sequence represent +expressions that use certain variables but equivalently we can say +these are functions that take these variables and return these expressions +that\textsf{'}s more convenient when you implement this in code so what we +need is a function that takes a to C and B to C and returns a function +from a plus B to C and this is the code that does it we take an argument +of type a plus B and we return a match expression if it\textsf{'}s in the left +we applied t1 to that value and we get to see if it\textsf{'}s in the right +we apply t2 to that value and we get a C so in any case we get a syllabus +so this is a function from a plus B to C as required another example +is the proof transformer for this rule this rule has one sequence +going to one sequence so in order to transform is proof into this +we need a function that takes argument of type A to B to C to D and +returns a function of type tuple a B going to C to D so here\textsf{'}s the +code we take a function f of type A to B to C to D we return a function +that takes a G of this type shown here in blue and return we need +to return a D so how do we get a deal we apply F to a function of +type A to B to C so we create that function out of G X of type a going +to Y of type B going to G of x1 so this is a function of type A to +B to C which is the argument of F as required and the result is of +type D so that is what we write so this kind of code is the proof +transformer for this derivation arrow and we need to produce this +proof transformers for every rule of the calculus lgt and I have done +it because I have implemented the Korea Howard library that uses LG +T so I'll must have done it for each flow this is a bit tedious because +there are many of those rules and you need to implement all this machinery +of passing arguments no matter how many in this gamma which are emitted +from this notation for brevity but in of course in the real code you +have to deal with all that too so let\textsf{'}s see how this works on an example +because once the proof tree is found we need to start backwards from +the leaves of the tree back to the root on each step we take the proof +expression apply the proof transformer to ative according to the rule +that was used on that step we get a new proof expression and so on +so for each sequence we will get a proof expression and at the end +we'll have a proof expression for the root sequence and that will +be the answer so I will denote denote by T I the proof expressions +for the sequence s hi so starting from s6 s6 was this sequence in +our proof so I mean yes just just going through the proof example +it was here backwards from a 6 back to a 0 s-six was this it followed +from axiom identity it\textsf{'}s proof expression t6 is a function of two +variables these two variables of these two types and this function +just returns the second variable so it\textsf{'}s a function of RR q and r +and just denote this by our argued and Garibaldi\textsf{'}s types r RQ variable +of this type is hard here so this function is very simple just ignores +the first argument and returns or so that is what the axiom does the +next sequence was as to as to was obtained by rule our implication +or right implication from s 6 so the proof transformer for right implication +let\textsf{'}s look at the right implication and see what the proof transformer +must be so we are given this sequence for this expression which is +the function body the function body that uses a variable of type a +somehow out of this we need to produce a function expression that +takes an argument of type a and returns that functional body so this +is the code which is just writing a new argument returning the function +body that was our proof transformer we need to convert function body +into a function so we just write that argument and arrow in the function +body so in our case we need this as a function body and so our t2 +is a function of our Q and this function is this the sequence s 3 +followed from the axiom and so it was just this function this is just +the identity function then we used the left implication so this was +actually still done in the calculus algae but the same thing works +in the calculus lgt I'm just using algae because it\textsf{'}s simpler for +example here proof transformer for the left implication is a little +more complicated and so if you look at it what what does it have to +be it takes these two expressions and returns this expression so it +takes a function from A to B to a and from B to C and it returns a +function from A to B to see how does it do it given a function a to +b you use this to derive a from it then you substitute that a into +the function into B you get a B when you use this to derive see from +that B and that\textsf{'}s your C so you use this function a to be twice you +put it in here once and then you get an A and substitute back into +the same function when you get a B then you use that and that\textsf{'}s exactly +what the proof transformer does it takes this rrq and it uses it twice +substitutes into it something that was obtained from one of the terms +and then uses the second term on the result so then this is the proof +transformer for the rule left implication the result of the proof +transformation is the proof for the sequence s1 finally we use the +right implication again which is just this function construction and +we get the proof expression for the sequence s0 now this proof expression +is written through these t1 t2 t3 we have to substitute all this back +in order to get the final expression so if we substitute first of +all we find this is our our cubone going to tea one of our cutie one +of our queue is this so we have to put it here now t3 is just identity +so we can just remove that so that gets you riq going to our Q of +T 2 T 2 is less if I have to put it in T 6 is just identity on R so +this is our going to our and so finally you have this expression so +that is the final code that has the required type notice that we have +derived this code completely algorithmic to it there was no guessing +we found which rules applied to the sequence with transformed sequence +according to the rules once we found the proof which was if we use +the calculus ljt the proof will be just a finite tree with no loops +it will terminate you can get an exhaustive depth-first search for +it for example and you find all the possible proofs if you want as +well well you will find many in any case in some for some expressions +and then we use the proof transformers which are fixed functions that +you can upfront compute for each these expressions are proof transformers +applied to the previous proofs so these are completely fixed algorithmically +fixed so we have derived this code completely algorithmically given +this expression this type so it is in this way that the career Howard +correspondence allows us to derive the code of functions from there +type signatures another important application of the correspondence +is to analyze type by some morphisms or type equivalences and I was +led to this by asking the question so in this logic or in the types +are these operations plus and times as I denoted them more like logic +more like the disjunction and conjunction or are they more like arithmetic +plus and times because this is kind of not so clear right away our +logic is this intuitionistic logic it in any case this is different +from boolean logic so what are the properties of these types really +so are the properties such that it is better to think about these +operations as plus and times rather than logical conjunction and disjunction +can answer this question I looked at identities that we have in the +water these are some identities from simple ones obvious ones to less +obvious identities like this the equal sign here stands for implication +in both directions so both this implies that and vice versa because +of this each of the implications means a function so since these are +all identities in logic it means that for example the implication +from here to here is a theorem of logic and so it can be implemented +as we know all our identities in logic can be implemented in code +and we even have an algorithm now that can automatically produce proofs +and automatically produce code so that means for any of these identities +that has some ik some expression X on the left and some Y on the right +so some kind of X equals y we have X implies Y and y implies X if +we convert that to code we will have a pair of functions function +from X to one and the function from Y to X what do these functions +do well they convert values in some ways from type X to type Y and +back so do these functions Express the equivalence of the types x +and y so that any value of type X can be converted to some equivalent +value type while and back without any loss of information is that +so that was the question I asked I looked at some examples well first +what does it mean more rigorously that types are equivalent for as +mathematicians say isomorphic the types are isomorphic and we will +use this notation for that if there is a one-to-one correspondence +between the sets of values of these types and in order to demonstrate +that we need a pair of functions one going from A to B the other going +from B to a such that the composition of these functions in both directions +is equal to identity function so F compose G or F value G will give +you from A to B and then from B to a is back so that would be identity +of a to a this will be identity of B to B if this is true if the composition +is identity it means we indeed did not lose any information let\textsf{'}s +consider an example this is an identity in the logic a conjunction +with one is equal to a in Scala the types responding to the left and +the right hand sides of this conjunction all of this are equivalent +are the conjunction of a and unit and a itself now we need functions +with these types indeed we can write functions is having these types +a pair of a and unit we need to produce an a out of that we'll just +take the first element of the pair you are done take an X of type +a will produce tuple of a and unit very easy just put a unit value +in the tuple in here done and it\textsf{'}s easy to verify that composition +of these functions will not change any values so it will be identity +in both directions another example this is an identity in logic if +this is understood as a disjunction one or a or true or a is true +that is an identity in logic for theorem in the logic are the types +equivalent though the type for 1 plus a is the option in Scala it +is option in Haskell at is called maybe this type is standard library +type in pretty much every functional programming language now option +of a is a disjunction of one or unit and a it is certainly not equivalent +to just unit because this type could contain a value of a in it but +this could not so there is no way that you could transform this type +to this and then back without losing information you could transform +so since this is a theorem you have functions from this type to this +type and back some functions you have them but these functions do +not compose to identity they cannot because what if you had a here +you must map it into unit from this unit back you must map into this +unit you cannot get an a out of unit and so that will erase this information +and that cannot become isomorphism so we see that some logic identities +do yield isomorphism types but others do not why is that let\textsf{'}s look +at some more examples to figure out why in all these examples we can +implement functions F 1 and F 2 between the two sets to two types +in both directions and then we can check we certainly can implement +them because these are logical identities but then we can check if +the compositions are identity functions and if so the types are isomorphic +but we find that in the first three examples we can do it but in this +last example we can note now I have written the logical identities +logical theorems with the arithmetic notation I call this arithmetical +notation because this suggests arithmetic operations plus and times +and if you look at these identities this looks like a well-known algebraic +identity from the school algebra in this too but this certainly seen +your own as an arithmetic as an as an arithmetic identity this is +certainly not true in arithmetic it is true in logical if you replace +this with disjunction and this with conjunction this is an identity +in logic so this suggests an interesting thing if you replace disjunction +by plus and conjunction by x and the result is an identity in arithmetic +then it is an isomorphism of types otherwise it is not let\textsf{'}s see why +this is so indeed this is so I call this the arithmetic arithmetic +oh very hard correspondence to see how it works let\textsf{'}s consider only +the types without loss of generation of generality that have a finite +set of possible values for example a boolean type has only two possible +true and false integer let\textsf{'}s say in the computers all the integers +are fine nights ago so those types have a finite set of possible values +and this does not limit our generality because in the computer everything +is finite all types have a finite set of possible values now let\textsf{'}s +consider how many values a given type has so that would be the size +of the type or using the mathematical terminology it\textsf{'}s called a cardinality +of the type so let\textsf{'}s see what is the cardinality of various type constructions +the sum type for example if the cardinality of types a and B is known +and the cardinality of a plus B the sum type the disjunction of a +and B is the sum of the two cardinalities or sizes this is because +a value of the disjunction type is constructed as either a value of +the first part or a value of the second part and so you cannot have +both together and so obviously the different number of values is just +the sum of the two sizes that the number of different values of the +sum type is just the sum of the numbers of different values of types +a and B for the product type again we have an interesting thing it\textsf{'}s +the arithmetic product of the sizes of a and B because for every a +value you could have an arbitrary B value so this is a direct product +or transient product of sets and we have school level identities about +the operations plus and times such as these identities or these all +of these identities are valid for arithmetic and they show if you +translate that into statements about the sizes of types they show +that the size of the type on the left is equal to the size of the +type on the right and that is very suggestive in other words if you +take a identity like this and you compute the size of the type on +the left and the size of the type on the right you get an arithmetic +identity of the sizes but you don't get that identity here because +the earth medical formula is not right this is very suggestive if +the sizes are equal and maybe the types are equivalent or isomorphic +when the sizes are not equal then certainly they cannot be equivalent +the function type very interestingly also is described in the same +way it provides the set of all maps between the two sets of values +so for example from integer to boolean that would be all the functions +that take some integer and return some boolean so that\textsf{'}s and a number +of boolean values \textasciicircum{} the number of integer values +that\textsf{'}s how many different functions you can have as a combinatorial +number so it\textsf{'}s an exponential and so the size of the type of function +a to be is the size of the type of B \textasciicircum{} the size of +type of a and again we have all the school identities about powers +and how to multiply powers and so on and they are directly translated +into these three identities if you take the sizes of the types on +the left and on the right the sizes will be equal due to these three +identities since the sizes are equal it\textsf{'}s very likely that the type +our actual equivalent so far haven't seen any counter examples to +this in these constructions so this gives us a meaning of the Curie +Howard correspondence so far we have seen three facets of the curly +Howard correspondence one is the correspondence between types and +logical formulas two is the correspondence between code and proofs +and three the correspondence between the cardinality of a type or +the set size of the type and the arithmetic identities that we have +in the school algebra about these types so arithmetical identities +signify type equivalence or isomorphism while logic identities only +talk about how you create some value of this type out of value of +another type so that does not guarantee that it preserves information +it just guarantees that you can implement some function of that type +it doesn't tell you that the function will be an isomorphism so if +one type is logically equivalent to another it means are equally implementable +if one is implementable another is also implementable but no more +than that whereas arithmetical identities actually tell you about +isomorphism of types therefore if you look at types and write them +using my preferred notation which is using the arithmetic all symbols +instead of logical symbols instead of these I'll use these symbols +if I do that this is very suggestive of a possible isomorphism of +types then it becomes very easy for me to reason about types I can +see right away that these two are isomorphic types or that these two +are isomorphic types because I am used to looking at school algebra +it\textsf{'}s very obvious then that this is not an isomorphism of types because +this doesn't make sense in the school algebra so reasoning about isomorphic +types is basically school level algebra involving polynomials and +powers so if you are familiar with all these identities as you should +be it will be very easy for you the reason about what types are equivalent +as long as all these types are made up of constants or primitive types +disjunctions tuples or conjunctions and functions which will then +directly be translated into exponential polynomial expressions constants +sums products and expand powers or Exponential\textsf{'}s so I call these exponential +polynomial types that is types built up from these type constructions +so all we have been talking about in this tutorial is what I call +exponential polynomial types these are the basic type constructions +that I started with tuple product function exponential disjunction +some unit constant or 1 now just one comment that in the functional +programming community today there is a terminology algebraic types +so people usually call algebraic types the types that are made from +constant types sums and products excluding Exponential\textsf{'}s I do not +find this terminology it\textsf{'}s very helpful I find it confusing because +what is particularly an algebraic about these identities these are +identities of school algebra the properties of the function type are +described by algebraic identities like this so it would be strange +to call the function type not algebraic whereas these types are algebraic +they are very similar to each other in terms of their properties being +described by identity is known from school algebra so instead of algebraic +types I would prefer to say polynomial types this is much more descriptive +and precise and if you want to talk about function types as well then +you just can you can just say exponential polynomial types or exfoli +types for short so by way of summarizing what we have done so far +what are the practical implications of the career Howard correspondence +so one set of implications is actually for writing code and reason +and eternal code one thing we can do now is if we're given a function +with some type and usually this will be typed with type parameters +all type trainers fully parametric types such as the function we have +been considering here all these functions do not have any types that +are specific like integer or string all the types are fully parametric +and then there are some constructions some type expressions made out +of these types so these are what I call fully parametric functions +for these functions we have a decision procedure an algorithm that +based on the ljt calculus which decides whether this function can +be implemented in code and computer scientists a type is inhabited +if you can produce a value of this type in your program so CH of T +is this proposition which they call type is inhabited and I prefer +to call it just that you can compute a value of this type or code +has the type O code can create a value of this type and so we have +a algorithm that can also generate the code from type when it is possible +if it is not possible the algorithm will tell you so often not always +but often this algorithm can be used actually to generate the code +you want we can also use what I call the arithmetic of glory Harvard +correspondence to reason about type isomorphisms and to transform +types isomorphic we simplify type expressions just like we simplify +expressions in school level algebra by expanding brackets by permuting +the order of terms like a plus B is equal to B plus a or associativity +a times B all times C can be expanded and so on so this allows us +once we have written types in the short notation in the notation that +I prefer which resembles school algebra because it uses the plus and +times symbols instead of the logic symbols so once we rewrite our +types and this notation which I have been doing consistently in this +tutorial it enables us the reason very easily but which types are +equal or isomorphic because we are all familiar with the school level +algebra what are the problems that we cannot solve using this knowledge +one thing we cannot do is to generate code automatically such that +it will be an isomorphism so for instance in an example here we are +able to generate automatically the code of these functions but it +will not be an isomorphism and the lgt algorithm cannot check that +this is nice a morphism that\textsf{'}s the important thing this algorithm +does not know about equations or isomorphisms it only knows that it +found some code that has the type you wanted whether this code is +useful to you or not we don't know the algorithm doesn't know this +also if the algorithm finds several such several proofs of a sequence +it will generate several not in equivalent versions of your code it +doesn't know which one is is useful maybe some of them are useless +maybe not the algorithm cannot automatically decide that in general +another thing we cannot do is to express complicated conditions via +types such as that array is sorted the type system is not powerful +enough in all the languages I listed you need a much more powerful +type system such as that in the programming language interests or +add them or cook those are much more powerful type systems that can +express such complicated conditions but for those type systems there +is no algorithm that will generate code another thing we cannot do +is to generate code that has type constructors such as the map function +here\textsf{'}s an example in Scala this is a map function on a list so there\textsf{'}s +the list of a a is a type parameter and then we say dot map and map +has another type frame to be it takes a function from A to B for any +B so a is fixed but now from any B we can take a function from A to +B and generate a list of B so if we wrote this formula in the short +type notation this would look something like this I'm writing subscript +a because this is a type parameter so this is like an argument or +a type parameter I'm writing it like this and then from this this +is the first argument of the function and then there is a second argument +which is this F and that is another quantifier for B inside parentheses +so this formula has a quantifier inside so far we have been dealing +with formulas that have all quantifiers outside and so we never write +quantifiers explicitly but here we have to write them inside this +is a more powerful logic which is called first-order logic in other +words this is a logic where you have quantifiers anywhere in the formula +including inside the formula unfortunately this logic is undecidable +so there is no algorithm that we can use either to find the proof +and therefore code freedom type or to show that there is no proof +no code so we're kind of stuck in all these directions some more remarks +about the curry Harvard correspondence first is that only with parameterize +types we can get some interesting information out of it if we take +concrete types like integer then the proposition CH event meaning +that our code can have a value of type int it that\textsf{'}s always true can +always write any some integer value we don't need any previous data +for it so for all specific types all these propositions are always +choice completely void of information the only interesting part comes +when we start considering type variables if we start asking can we +make a type which is either of a B going to a going to B in soon for +all a B once we start doing this with type parameters a B and so on +then we get interesting information as we have seen in this tutorial +another remark is that functions like this one are not sufficiently +described by their type so that this is the type of integer going +to integer now looking at this type we can put this into a sequence +but we'll never get enough information to actually get this function +so only certain class of functions which are fully typed biometric +their type signature is informative enough so that we can derive code +automatically only in much more powerful type systems you can have +type information that is enough to specify fully a code like this +another caveat is that I don't know the proof that arithmetic identity +guarantees the type equivalence it is certainly a necessary condition +because if two types have different cardinality or different size +of their sets of values that they cannot be equivalent or they cannot +be isomorphic so this is a necessary condition but it\textsf{'}s not a sufficient +condition it looks like I don't know if this is sufficient I haven't +seen any counter examples so far final remarks about type correspondence +the logical constant false did not appear in any of my slides so far +this was on purpose it has extremely limited practical use in programming +languages because actually we have types corresponding to false Scala +has type called nothing Haskell has type usually called void that +corresponds to the logical constant false what does it mean CH of +nothing is false it means your code can never have a value of type +nothing or in Haskell void you can never compute a value of this type +so clearly it has a very limited practical significance you will never +be able to compute any values of this type ever in any program it\textsf{'}s +identically falseness this constant so if you want to add it to the +logic it\textsf{'}s very easy you just have one rule and you're not done you +can derive things with it if you want but they will have almost never +any use in practical code also we did not talk about negation none +of the calculus calculate that I should have in logical negation as +in operation again for the same reason we do not have a programming +language construction that represents logical negation negation by +definition is like this is an application from 8 to 4 so that\textsf{'}s not +a not a means from a follows falsehood now since you cannot ever get +false in a programming language you cannot really implement this function +in any useful sense and so i have seen some haskell library that used +this type void as a type parameter in some way but certainly it\textsf{'}s +a very limited and rare use and so it is not really lumen 18 to include +negation it could probably find some very esoteric uses of it but +almost never useful and finally there is another set of important +implications from the Kurihara correspondence these are implications +for people who want to design new programming languages as we have +seen the Karaka with correspondence maps the type system of a programming +language into a certain logical system where prepositions follow from +each other or can be proved from each other and this enables us to +reason about programmed to see what kind of code can be written if +some other kind of code can be written and logical reasoning is very +powerful it\textsf{'}s simpler than trying to write code and it gives you algorithms +and all kinds of mathematical results that have been found over the +centuries so languages like those listed here have all the five type +constructions that I wasted in the beginning of this tutorial and +mapping them into logic gives a full constructive logic or full intuitionistic +logic with all logical operations and or so conjunction disjunction +implication and the truth constant whereas languages such as C C++ +Java and c-sharp and so on they're mapped to incomplete logics because +they do not have some of these operations for instance they do not +have type constructions of correspond to disjunction we also do not +have the true constant or the false constant so they are mapped to +a logic that lacks some of the foundational logical operation so it +can be only fewer theorems can be proved in that logic and so your +reasoning about theory types is hampered languages called scripting +languages sometimes such as Python or JavaScript will be and so on +also our belongs there in that line those languages only have one +type they actually don't check types at compile time and so they're +mapped to logics with only one proposition those logics are extremely +small in terms of what kind of things you can reason about and so +if you write a program in these languages you are completely unable +to reason at the level of types whereas in these languages you are +able to reason but in a limited way you're not having a complete logic +so this suggests a principle for designing the type system in a new +programming language the first step would be to choose a good and +complete logic that is free of inconsistency mathematicians have studied +all kinds of logics and they are always interested in questions such +as is this logic consistent consistent means you cannot derive false +from true is this logic complete can you derive all things that are +true are there enough axioms and rules of derivation or maybe there +are too many axioms and rules of derivation you can delete some of +them and have fewer mathematicians have always been interested in +such questions they found all kinds of interesting logics where you +can derive a lot of interesting theorems non trivial theorems and +they found the minimum sets of axioms and rules of derivations for +these logics use their results take one of the logics that they do +them and develop such as intuitionistic logic model logic temporal +logic linear logic and so on take one of these logics for each of +the basic operations of this logic provide type constructions in your +programming language that are easy to use for instance your logic +has disjunction implication or something else provide a type constructor +for each of them that\textsf{'}s easy to use easy to write down such as provided +by the languages we have seen then every type will be mapped to a +logical form of the OPF logical formula for every type and there will +be a type for every logical formula and then for each rule of the +new logic for each derivation rule there should be a construct in +the code that corresponds to it so that you could transform proofs +in logic into code and code into proofs if you do that your language +will be faithful to the scorecard correspondence you will be able +to use logic to reason about your language and one important result +at this level while we have seen that you can sometimes generate code +that is maybe nice but a very important result is that if your logic +is free of inconsistency it means that no program will ever be able +to derive an inconsistent an inconsistent type means that you had +a function that requires some type a but it was called with a different +type beam which is incompatible and that basically crashes so in languages +like C and C++ we have all kinds of crashes like a segmentation fault +in Java the exceptions nullpointerexception or class cast exception +which happens when you call a function on the wrong type of argument +and that happens if your logic is inconsistent if your logic can derive +incorrect statements from correct premises then if you translate that +derivation into code and the that code will derive incompatible type +at the wrong place and it will crash the crash will happen at runtime +the compiler will not catch this inconsistency because the compiler +only checks the logic of types and the logic checks out you have followed +the rules of derivation of the logic the compiler can check out all +these logical rules but the compiler does not know that your logic +is inconsistent maybe and then it will deep have derived an inconsistent +result falsehood from truth for example and that will crash at runtime +now we know that crashing at runtime is not a good outcome so in fact +languages like Oh camel have been studied and for other languages +some subsets of Haskell I believe called safe Haskell have been studied +and it has been shown that they cannot crash and they're the way to +show it mathematically is to use the fact that they are based on a +complete and consistent logic and then all you need to show is that +your compiler does not have some critical bugs that allow it to oversee +that you have not followed the derivation rules of the logic that +is an extremely valuable feature of functional programming languages +that are based on the Curie habit correspondence you can prove their +safety at compile time or at least exclude a large number of possible +bugs and errors certainly these languages are quite large and they +include features that are not covered by the Carey Hart correspondence +type constructors that I have not considered in this tutorial and +those might may not be safe but at least the foundation of these languages +the foundation of the type system will be safe so that is the final +lesson from the great Howard correspondence this concludes the tutorial +\end{comment} -To \emph{prove} that there is no proof, one needs to use methods -that are beyond the scope of this book. An introduction to the required -techniques is in the book \textsf{``}Proof and Disproof in Formal Logic\textsf{''} -by R.~Bornat\index{Richard Bornat} (see footnote~\ref{fn:Bornat-proof-book} -on page~\pageref{fn:Bornat-proof-book}). diff --git a/sofp-src/sofp-disjunctions.lyx b/sofp-src/sofp-disjunctions.lyx index 37497a2e4..c1bded6c0 100644 --- a/sofp-src/sofp-disjunctions.lyx +++ b/sofp-src/sofp-disjunctions.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -452,8 +467,10 @@ opaque type. \end_layout \begin_layout Standard -Opaque types (hiding a type under a new name) is a feature of a future version - of Scala 3; so we focus on type aliases and case classes. +Opaque types (hiding a type under a new name) is a feature of Scala 3. + It can be seen as a case class with a single field but without the cost + of memory allocation. + Here, we will focus on type aliases and case classes. \end_layout \begin_layout Standard @@ -606,7 +623,7 @@ scala> val paid = Payment(25.0, "restaurant") \begin_layout Plain Layout paid: Payment = Payment(25.0,restaurant) - ^ + \end_layout \end_inset @@ -1192,21 +1209,6 @@ Int \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "48col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1223,23 +1225,6 @@ s: MySockX[Int] = MySockX(10.5,white,123) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Because the value \begin_inset listings inline true @@ -1821,8 +1806,7 @@ Let us compare tuples and case classes more systematically. \end_layout \begin_layout Standard -Parts of a case class are accessed by name with a dot syntax, for example - +Parts of a case class are accessed with a dot syntax, for example \begin_inset listings inline true status open @@ -2174,8 +2158,7 @@ case class C() // Tuple with no parts. \end_layout \begin_layout Standard -The following table summarizes the correspondence between tuples and case - classes: +The following table shows the correspondence between tuples and case classes: \end_layout \begin_layout Standard @@ -2419,7 +2402,7 @@ case object \end_inset - cannot have type parameters, while we may define a + cannot have type parameters, while we could define a \begin_inset listings inline true status open @@ -2467,7 +2450,7 @@ Z \end_inset - if needed. +, etc. \end_layout \begin_layout Itemize @@ -2781,7 +2764,7 @@ def fits(bag: BagOfSocks): Boolean = bag match { \end_inset -In the code of this function, we match the +In the code of this function, the value of \begin_inset listings inline true status open @@ -2793,7 +2776,7 @@ bag \end_inset - value against the pattern + is matched with the pattern expression \begin_inset listings inline true status open @@ -2806,7 +2789,7 @@ BagOfSocks(MySock(size, _), _) \end_inset . - This pattern will always match and will define + This pattern will define \begin_inset listings inline true status open @@ -2828,9 +2811,89 @@ status open Double \end_layout +\end_inset + + and assign its value to the corresponding part of the case class. + For example, the value +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +BagOfSocks(MySock(10.5, +\begin_inset Quotes eld +\end_inset + +white +\begin_inset Quotes erd +\end_inset + +), 6) +\end_layout + +\end_inset + +) matched against +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +BagOfSocks(MySock(size, _), _) +\end_layout + +\end_inset + + assigns +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +size +\end_layout + +\end_inset + + to +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +10.5 +\end_layout + \end_inset . + The symbol +\begin_inset Quotes eld +\end_inset + + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +_ +\end_layout + +\end_inset + + +\begin_inset Quotes erd +\end_inset + + means that we just ignore other parts of the case classes and do not create + any pattern variables for them (because we do not need them in this specific + code). \end_layout \begin_layout Standard @@ -2877,8 +2940,8 @@ status open \end_inset . - We see that within pattern matching expressions, case classes behave as - tuple types with added names. + So, within pattern matching expressions, case classes behave as tuple types + with added names. \end_layout @@ -3439,38 +3502,38 @@ RootsOfQ \end_inset - is a value of type + is a value of the form \begin_inset listings inline true status open \begin_layout Plain Layout -case class NoRoots() +NoRoots() \end_layout \end_inset -, or a value of type +, or of the form \begin_inset listings inline true status open \begin_layout Plain Layout -case class OneRoot(x: Double) +OneRoot(x: Double) \end_layout \end_inset -, or a value of type +, or of the form \begin_inset listings inline true status open \begin_layout Plain Layout -case class TwoRoots(x: Double, y: Double) +TwoRoots(x: Double, y: Double) \end_layout \end_inset @@ -3490,26 +3553,26 @@ SearchResult \end_inset - is a value of type + is a value of the form \begin_inset listings inline true status open \begin_layout Plain Layout -case class Index(x: Int) +Index(x: Int) \end_layout \end_inset - or a value of type + or of the form \begin_inset listings inline true status open \begin_layout Plain Layout -case class NotFound() +NotFound() \end_layout \end_inset @@ -3529,26 +3592,26 @@ Result \end_inset - is a value of type + is a value of the form \begin_inset listings inline true status open \begin_layout Plain Layout -case class Value(x: Int) +Value(x: Int) \end_layout \end_inset - or of type + or \begin_inset listings inline true status open \begin_layout Plain Layout -case class Error(message: String) +Error(message: String) \end_layout \end_inset @@ -3608,7 +3671,7 @@ TwoRoots \end_inset - are the three alternatives allowed by the type + are the only possibilities allowed by the type \begin_inset listings inline true status open @@ -3828,17 +3891,17 @@ sealed \end_inset - tell the Scala compiler that the set of case classes within a disjunctive + tell the Scala compiler that the given set of case classes within a disjunctive type is fixed and unchangeable. \end_layout \begin_layout Subsection -Solved examples: Pattern matching for disjunctive types +Examples: Pattern matching for disjunctive types \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -4080,24 +4143,6 @@ case patterns because we need to match several possible cases of the disjunctive type: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -4175,20 +4220,6 @@ one real root: 2.0 \end_inset - -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Each \begin_inset listings inline true @@ -4865,8 +4896,7 @@ map \end_inset - instead of defining this function separately. - This avoids having to destructure +, destructuring \begin_inset listings inline true status open @@ -4878,7 +4908,7 @@ QEqu \end_inset - at a separate step. + at the same time. The \begin_inset listings inline true @@ -5302,7 +5332,7 @@ NotFound() \end_inset in that case. - The new code can be written as a + We use a \begin_inset listings inline true status open @@ -5326,7 +5356,7 @@ case \end_inset - expression for clarity: + expression for the new logic: \begin_inset listings inline false status open @@ -5795,7 +5825,8 @@ safe arithmetic \begin_inset Quotes erd \end_inset - on simple calculations: + on simple calculations. + Let us see what happens after an error: \begin_inset listings inline false status open @@ -5826,8 +5857,7 @@ res11: Result[Int] = Error(error: 3 / 0) \end_inset -We see that indeed all further computations are abandoned once an error - occurs. +Indeed, all further computations are abandoned once an error occurs. An error message shows only the immediate calculation that generated the error. For instance, the error message for @@ -5974,8 +6004,8 @@ Option \end_inset - type were not already defined in the standard library, one could define - it with the code: + type were not already defined in the Scala library, we could define it + by: \begin_inset listings inline false status open @@ -6341,17 +6371,16 @@ Option \end_inset is used in situations where a value may be either present or missing, especiall -y when a missing value is +y when a missing value +\emph on +is +\emph default + \emph on not an error \emph default . - -\begin_inset Note Comment -status open - -\begin_layout Plain Layout -The missing-value case is represented by + The missing-value case is represented by \begin_inset listings inline true status open @@ -6375,7 +6404,7 @@ Some(x) \end_inset - means that a value + represents a value \begin_inset listings inline true status open @@ -6387,12 +6416,7 @@ x \end_inset - is present. -\end_layout - -\end_inset - - + that is present. \end_layout \begin_layout Subsubsection @@ -6418,7 +6442,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -6558,8 +6582,7 @@ def getDigits(phone: Option[Long]): Option[Seq[Long]] = phone match { \begin_layout Plain Layout - case None => None // Cannot obtain digits, so return - `None`. + case None => None // Have no digits, so return `None`. \end_layout \begin_layout Plain Layout @@ -8908,7 +8931,7 @@ Left status open \begin_layout Plain Layout -These methods are available in Scala 2.12 or a later version. +These methods are available in Scala 2.12 and in later versions. \end_layout \end_inset @@ -9136,7 +9159,7 @@ unplanned exception \end_inset is generated by the Java runtime system when critical errors occur, such - as an out-of-memory error. + as a stack overflow or an out-of-memory error. It is rare that a programmer writes \begin_inset listings inline true @@ -9153,20 +9176,21 @@ val y = f(x) \emph on expecting \emph default - that an out-of-memory exception will sometimes occur at that point. + that an out-of-memory exception will likely occur at that point. \begin_inset Foot status open \begin_layout Plain Layout Just once in the author's experience, an out-of-memory exception had to - be anticipated in an Android app. + be anticipated in an Android app as something that regularly happens during + normal usage of the app. \end_layout \end_inset - An unplanned exception indicates a serious and unforeseen problem with - memory or another critically important resource, such as the operating - system's threads or file handles. + An unplanned exception indicates a serious problem with memory or another + critically important resource, such as the operating system's threads or + file handles. Such problems usually cannot be fixed and will prevent the program from running any further. It is reasonable that the program should abruptly stop (or @@ -9186,6 +9210,10 @@ The use of planned exceptions assumes that the programmer will write code This assumption makes it significantly harder to write programs correctly: it is hard to figure out and to keep in mind all the possible exceptions that a given library function may +\begin_inset Quotes eld +\end_inset + + \begin_inset listings inline true status open @@ -9197,7 +9225,11 @@ throw \end_inset - in its code (and in the code of all other libraries on which it depends). + +\begin_inset Quotes erd +\end_inset + + in its code (and in the code of all other libraries being used). Instead of using exceptions for indicating errors, Scala programmers can write functions that return a disjunctive type, such as \begin_inset listings @@ -9217,13 +9249,13 @@ Either have \emph default to do pattern matching on the result values. - This helps programmers to remember and to handle all relevant error situations - that the programmers anticipate to encounter. + This helps programmers to avoid forgetting to handle an error situation + that the code is likely to encounter. \end_layout \begin_layout Standard -However, programmers will often need to use Java or Scala libraries that - +Nevertheless, programmers will often need to use Java or Scala libraries + that \begin_inset listings inline true status open @@ -9236,20 +9268,8 @@ throw \end_inset exceptions. - To help write code for these situations, the Scala library contains a helper - function called -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Try() -\end_layout - -\end_inset - - and a disjunctive type, also called + To help write code for these situations, the Scala library provides a disjuncti +ve type called \begin_inset listings inline true status open @@ -9388,7 +9408,7 @@ Either \end_inset type). - The function + The class constructor \begin_inset listings inline true status open @@ -9400,7 +9420,15 @@ Try(expr) \end_inset - will catch all exceptions thrown while the expression + will catch all +\begin_inset Quotes eld +\end_inset + +planned +\begin_inset Quotes erd +\end_inset + + exceptions thrown while the expression \begin_inset listings inline true status open @@ -9413,7 +9441,45 @@ expr \end_inset is evaluated. - If the evaluation of +\begin_inset Foot +status open + +\begin_layout Plain Layout +But +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Try() +\end_layout + +\end_inset + + will not catch exceptions of class +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +java.lang.Error +\end_layout + +\end_inset + + and its subclasses. + Those exceptions are intended to represent unplanned, serious error situations. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +If the evaluation of \begin_inset listings inline true status open @@ -9486,7 +9552,7 @@ t: Throwable \end_inset - is the value associated with the generated exception. + is the value containing the details about the exception. Here is an example of using \begin_inset listings inline true @@ -9739,24 +9805,6 @@ flatMap \end_inset , and other standard methods: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "57col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -9787,20 +9835,6 @@ z: Try[Int] = Failure(java.lang.Exception: huh) \end_inset - -\begin_inset VSpace -90baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In this example, the values \begin_inset listings inline true @@ -9946,24 +9980,6 @@ NInt \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "46col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -9985,20 +10001,6 @@ final case class N2(n: NInt) extends NInt \end_inset - -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The type \begin_inset listings inline true @@ -10011,7 +10013,7 @@ NInt \end_inset - has two disjunctive parts, + has two disjunctive parts: \begin_inset listings inline true status open @@ -10427,8 +10429,11 @@ List[A] via mathematical induction on the length of the list: \end_layout -\begin_layout Itemize -Base case: empty list, +\begin_layout Standard +\begin_inset Formula $\bullet$ +\end_inset + + Base case: empty list, \begin_inset listings inline true status open @@ -10443,8 +10448,11 @@ case class List0[A]() . \end_layout -\begin_layout Itemize -Inductive step: given a list of a previously defined length, say +\begin_layout Standard +\begin_inset Formula $\bullet$ +\end_inset + + Inductive step: given a list of a previously defined length, say \begin_inset listings inline true status open @@ -11061,7 +11069,7 @@ def headOption[A]: ListI[A] => Option[A] = { \end_layout \begin_layout Standard -The Scala library already defines the type +The Scala library defines the type \begin_inset listings inline true status open @@ -11073,7 +11081,7 @@ List[A] \end_inset - in a different but equivalent way: + in a slightly different way: \begin_inset listings inline false status open @@ -12143,7 +12151,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -12665,7 +12673,7 @@ def reverse[A](xs: NEL[A]): NEL[A] = \begin_layout Plain Layout -scala> reverse(toNEL(10,List(20,30))) // The result is [30, 20, 10, 10]. +scala> reverse(toNEL(10, List(20, 30))) // The result is [30, 20, 10, 10]. \end_layout \begin_layout Plain Layout @@ -12943,8 +12951,7 @@ def map[A,B](xs: NEL[A])(f: A => B): NEL[B] = ??? \begin_layout Plain Layout -scala> map[Int, Int](toNEL(10, List(20, 30)))(_ + 5) // The result is [15, - 25, 35]. +scala> map[Int, Int](toNEL(10, List(20, 30)))(_ + 5) \end_layout \begin_layout Plain Layout @@ -12979,26 +12986,14 @@ noprefix "false" \end_layout \begin_layout Standard -Implement a function -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -concat -\end_layout - -\end_inset - - that concatenates two non-empty lists: +Implement a function that concatenates two non-empty lists: \begin_inset listings inline false status open \begin_layout Plain Layout -def concat[A](xs: NEL[A], ys: NEL[A]): NEL[A] = ??? +def concatNEL[A](xs: NEL[A], ys: NEL[A]): NEL[A] = ??? \end_layout \begin_layout Plain Layout @@ -13007,8 +13002,7 @@ def concat[A](xs: NEL[A], ys: NEL[A]): NEL[A] = ??? \begin_layout Plain Layout -scala> concat(More(1, More(2, Last(3))), More(4, Last(5))) // The result - is [1, 2, 3, 4, 5]. +scala> concatNEL(More(1, More(2, Last(3))), More(4, Last(5))) \end_layout \begin_layout Plain Layout @@ -13119,7 +13113,11 @@ Tree[ [ [ $a_1$ ] [ [ $a_2$ ] [ $a_3$ ] ] ] [ [ $a_4$ ] [ $a_5$ ] ] ] \size default , where -\begin_inset Formula $a_{i}$ +\begin_inset Formula $a_{1}$ +\end_inset + +, ..., +\begin_inset Formula $a_{5}$ \end_inset are some values of type @@ -13192,11 +13190,11 @@ Here are some examples of code expressions and the corresponding trees that lines 0 placement l overhang 0in -width "60col%" +width "73col%" status open \begin_layout Plain Layout -\begin_inset VSpace -40baselineskip% +\begin_inset VSpace -60baselineskip% \end_inset @@ -13268,7 +13266,7 @@ Tree[ [ [ $a_1$ ] [ $a_2$ ] ] [ $a_3$ ] ] lines 0 placement l overhang 0in -width "60col%" +width "73col%" status open \begin_layout Plain Layout @@ -13336,7 +13334,7 @@ a5 \end_layout \begin_layout Plain Layout -\begin_inset VSpace 140baselineskip% +\begin_inset VSpace 110baselineskip% \end_inset @@ -13348,7 +13346,7 @@ a5 \end_layout \begin_layout Standard -\begin_inset VSpace 40baselineskip% +\begin_inset VSpace 30baselineskip% \end_inset @@ -13570,7 +13568,7 @@ rose tree of trees. Because of that, a rose tree can fork into arbitrarily many branches at each node, rather than always into two branches as the binary tree does. - For example, + Some examples of rose trees are \begin_inset Preview \begin_layout Standard @@ -13583,7 +13581,8 @@ status open \backslash -Tree[ [ [ $a_1$ ] [ $a_2$ ] [ $a_3$ ] ] [ [ $a_4$ ] [ $a_5$ ] ] ] +Tree[ [ [ $a_1$ ] [ $a_2$ ] [ $a_3$ ] ] [ [ $a_4$ ] [ $a_5$ ] [ $a_6$ ] + ] ] \end_layout \end_inset @@ -13606,7 +13605,7 @@ status open \backslash -Tree[ [ $a_1$ ] [ $a_2$ ] [ $a_3$ ] [ $a_4$ ] ] +Tree[ [ $a_1$ ] [ [ $a_2$ ] [ $a_3$ ] ] [ $a_4$ ] [ $a_5$ ] ] \end_layout \end_inset @@ -13616,7 +13615,7 @@ Tree[ [ $a_1$ ] [ $a_2$ ] [ $a_3$ ] [ $a_4$ ] ] \end_inset - are rose trees. + . \end_layout \begin_layout Standard @@ -13951,9 +13950,7 @@ Branch2 \end_inset , and so on. - Now, we cannot rewrite this definition as a recursive type because the - case classes do not have the same structure. - The non-trivial trick is to notice that each case class + The non-trivial step is to notice that each case class \begin_inset listings inline true status open @@ -13998,7 +13995,7 @@ A \end_inset . - So we can rewrite this definition as: + So we can rewrite the above definition as: \begin_inset listings inline false status open @@ -14373,7 +14370,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -14534,24 +14531,6 @@ xs . Let us try: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "61col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -14578,20 +14557,6 @@ def map[A, B](t: PTree[A])(f: A => B): PTree[B] = t match { \end_inset - -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Here, \begin_inset listings inline true @@ -16179,8 +16144,7 @@ What problems can we solve now? \end_layout \begin_layout Itemize -Represent values from a disjoint domain by a custom-defined disjunctive - type. +Represent values from a disjoint domain by a custom disjunctive type. \end_layout \begin_layout Itemize @@ -16224,12 +16188,12 @@ Either \end_inset - and their standard methods. + and their methods. \end_layout \begin_layout Itemize -Define recursive disjunctive types (e.g., lists and trees) and implement recursive - functions that work with them. +Define recursive disjunctive types (such as lists and trees) and implement + recursive functions that work with them. \end_layout \begin_layout Standard @@ -16237,12 +16201,12 @@ The following examples and exercises illustrate these tasks. \end_layout \begin_layout Subsection -Solved examples +Examples \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -16402,9 +16366,8 @@ DayOfWeek \end_inset - so that the values additionally represent a restaurant name and total amount - for Fridays and a wake-up time on Saturdays. - + so that the values additionally represent names of restaurants, the total + amount for Fridays, and the wake-up time on Saturdays. \end_layout \begin_layout Subparagraph @@ -16875,7 +16838,7 @@ status open \end_inset . - The code then becomes: + Then the code is: \begin_inset listings inline false status open @@ -17338,21 +17301,6 @@ Solution \begin_layout Standard Begin by pattern matching on the argument: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "62.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -95baselineskip% -\end_inset - - \begin_inset listings lstparams "numbers=left" inline false @@ -17380,23 +17328,6 @@ def f1[A, B]: Option[Either[A, B]] => Either[A, Option[B]] = { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -115baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In line 3, we wrote the \series bold type annotation @@ -17485,7 +17416,7 @@ status open \begin_layout Plain Layout -x:A +x: A \end_layout \end_inset @@ -17509,7 +17440,7 @@ status open \begin_layout Plain Layout -y:Option[B] +y: Option[B] \end_layout \end_inset @@ -17557,7 +17488,7 @@ status open \begin_layout Plain Layout -z:B +z: B \end_layout \end_inset @@ -17582,7 +17513,7 @@ status open \begin_layout Plain Layout -x:A +x: A \end_layout \end_inset @@ -17594,7 +17525,7 @@ status open \begin_layout Plain Layout -z:B +z: B \end_layout \end_inset @@ -17682,21 +17613,6 @@ eab \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "46col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -17729,22 +17645,9 @@ status open \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\noindent It remains to figure out what expressions to write in each case. In the case \begin_inset listings @@ -17802,7 +17705,7 @@ status open \begin_layout Plain Layout -x:A +x: A \end_layout \end_inset @@ -17826,7 +17729,7 @@ status open \begin_layout Plain Layout -y:Option[B] +y: Option[B] \end_layout \end_inset @@ -17895,21 +17798,6 @@ Left(a) \end_inset , the code is: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "65.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset listings lstparams "numbers=left" inline false @@ -17952,23 +17840,6 @@ def f1[A, B]: Option[Either[A, B]] => Either[A, Option[B]] = { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Let us decide whether to return \begin_inset listings inline true @@ -18026,7 +17897,7 @@ status open \begin_layout Plain Layout -a:A +a: A \end_layout \end_inset @@ -18057,7 +17928,7 @@ Left(a) \end_layout \begin_layout Standard -Reasoning similarly for line 5, we find that we may return +Similarly, we find in line 5 that we may return \begin_inset listings inline true status open @@ -18089,7 +17960,7 @@ status open \begin_layout Plain Layout -b:B +b: B \end_layout \end_inset @@ -18221,21 +18092,6 @@ Solution \begin_layout Standard Begin by pattern matching on the argument: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "60col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset listings lstparams "numbers=left" inline false @@ -18258,23 +18114,6 @@ def f2[A, B]: (Option[A], Option[B]) => Option[(A, B)] = { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In line 2, we have values \begin_inset listings inline true @@ -18805,7 +18644,7 @@ noprefix "false" \end_layout \begin_layout Standard -In the +In the context of the \begin_inset Quotes eld \end_inset @@ -18813,8 +18652,22 @@ Minesweeper \begin_inset Quotes erd \end_inset - game, count the total number of cells with zero neighbor bombs shown by - implementing a function with type signature + game (Exercise +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-disjunctive-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +), count the total number of cells with zero neighbor bombs shown by implementin +g a function with type signature \begin_inset listings inline true status open @@ -19541,8 +19394,8 @@ ListX[A] \end_inset - representing a data structure in the form of a list with one or more values - where the first value has type + representing a data structure in the form of a non-empty list where the + first value has type \begin_inset listings inline true status open @@ -19604,7 +19457,7 @@ Option[A] \end_inset for the second value in the list) should cause a type error. - Implement functions + Implement (not necessarily tail-recursive) functions \begin_inset listings inline true status open @@ -19641,6 +19494,24 @@ ListX \end_inset . + The type signatures: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def map[A, B](lx: ListX[A])(f: A => B): ListX[B] = ??? +\end_layout + +\begin_layout Plain Layout + +def foldLeft[A, R](lx: ListX[A])(init: R)(f: (R, A) = R): R = ??? +\end_layout + +\end_inset + + \begin_inset Note Note status open @@ -20039,10 +19910,10 @@ noprefix "false" \end_inset - cannot be correctly represented by the mathematical union + cannot be correctly represented by the mathematical union: \begin_inset Formula \[ -\mathbb{R}^{0}\cup\mathbb{R}^{0}\cup\mathbb{R}^{1}\cup\mathbb{R}^{0}\cup\mathbb{R}^{1}\cup\mathbb{R}^{2} +\mathbb{R}^{0}\cup\mathbb{R}^{0}\cup\mathbb{R}^{1}\cup\mathbb{R}^{0}\cup\mathbb{R}^{1}\cup\mathbb{R}^{2}\quad, \] \end_inset @@ -20063,7 +19934,7 @@ because \end_inset -This representation has lost the distinction between e.g., +For instance, this representation has no distinction between \begin_inset listings inline true status open @@ -20220,7 +20091,7 @@ status open \begin_layout Standard Labeled unions are not often used in mathematics, but they are needed in software engineering because real-life data is often described by sets - comprised of several disjoint parts. + having several disjoint parts. \end_layout \begin_layout Paragraph @@ -20238,9 +20109,9 @@ ed by a set containing one \emph default point. - Why should we not use an empty set (rather than a set with one point) to - represent the case where the equation has no real roots? The reason is - that we are required to represent not only the values of the roots but + Why should we not use an empty set (rather than a set with one element) + to represent the case where the equation has no real roots? The reason + is that we are required to represent not only the values of the roots but also the information \emph on about @@ -20377,7 +20248,7 @@ NoRoots() \end_layout \begin_layout Standard -So, case classes with no parts are quite similar to +So, case classes with no parts are similar to \begin_inset listings inline true status open @@ -20467,9 +20338,9 @@ Disjunctive types in other programming languages Disjunctive types and pattern matching turns out to be one of the defining features of FP languages. Languages that were not designed for functional programming do not support - these features, while ML, OCaml, Haskell, F#, Scala, Swift, Elm, and PureScript - support disjunctive types and pattern matching as part of the language - design. + these features, while ML, OCaml, Haskell, F#, Scala, Swift, Elm, PureScript, + and Rust support disjunctive types and pattern matching as part of the + language design. \end_layout @@ -20492,27 +20363,17 @@ records ) are provided in almost every programming language, while disjunctive types are almost never present except in languages designed for the FP paradigm. - (Ada and Pascal are the only languages that support disjunctive types without - other FP features. \begin_inset Foot status open \begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(basic_instructions)#Other_types" -literal "false" - -\end_inset - - +The programming languages Ada and Pascal support disjunctive types but no + other FP features. \end_layout \end_inset -) + \end_layout \begin_layout Standard @@ -20555,7 +20416,8 @@ union { int x; double y; long z; } i_d_l; \end_inset -Without a label, we (and the compiler) will not know whether a given value +This type does not include any label telling us which of the values is present. + Without a label, we (and the compiler) will not know whether a given value of type \begin_inset listings inline true @@ -20705,15 +20567,7 @@ final case class BLUE() extends Color \end_inset -If we -\begin_inset Quotes eld -\end_inset - -enrich -\begin_inset Quotes erd -\end_inset - - the +If we add extra data to the \begin_inset listings inline true status open @@ -20725,8 +20579,7 @@ enum \end_inset - types with extra data, so that the tuples could be non-empty, and extend - the + types, allowing the tuples to be non-empty, and extend the \begin_inset listings inline true status open @@ -20738,7 +20591,7 @@ switch \end_inset - expression to be able to handle the extra data, we will obtain the full + expression to be able to handle the extra data, we will recover the full functionality of disjunctive types. A definition of \begin_inset listings @@ -20774,7 +20627,7 @@ enum RootsOfQ { // This is not valid in Java! \end_inset -A future version of Scala 3 will have a syntax for disjunctive types +Scala 3 has a shorter a syntax for disjunctive types \begin_inset Foot status open @@ -20794,11 +20647,11 @@ literal "false" \end_inset - that resembles + that resembles Java's \begin_inset Quotes eld \end_inset -enriched + \begin_inset listings inline true status open @@ -20821,8 +20674,27 @@ status open \begin_layout Plain Layout -enum RootsOfQ { case NoRoots; case OneRoot(x: Double); case TwoRoots(x: - Double, y: Double) } +enum RootsOfQ { +\end_layout + +\begin_layout Plain Layout + + case NoRoots +\end_layout + +\begin_layout Plain Layout + + case OneRoot(x: Double) +\end_layout + +\begin_layout Plain Layout + + case TwoRoots(x: Double, y: Double) +\end_layout + +\begin_layout Plain Layout + +} \end_layout \end_inset @@ -20894,7 +20766,7 @@ noprefix "false" will introduce a mathematical notation designed for efficient reasoning about types. - That notation is still more concise than Haskell or OCaml. + That notation is even more concise than Haskell or OCaml. \end_layout \begin_layout Subsection @@ -21265,40 +21137,9 @@ disjunctive types status open \begin_layout Plain Layout -Disjunctive types are also known as -\begin_inset Quotes eld -\end_inset - -variants -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - -sum types -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - -co-product types -\begin_inset Quotes erd -\end_inset - -, and -\begin_inset Quotes eld -\end_inset - -tagged union types -\begin_inset Quotes erd -\end_inset - -. - This book calls them +Disjunctive types are also called variants, sum types, co-product types, + and tagged unions. + This book uses the terms \begin_inset Quotes eld \end_inset diff --git a/sofp-src/sofp-disjunctions.tex b/sofp-src/sofp-disjunctions.tex index 309d3d303..f2cb1c500 100644 --- a/sofp-src/sofp-disjunctions.tex +++ b/sofp-src/sofp-disjunctions.tex @@ -33,8 +33,10 @@ \subsection{Tuple types with names} a new named type in Scala: using a type alias, using a class (or \textsf{``}trait\textsf{''}), and using an \index{opaque type}opaque type. -Opaque types (hiding a type under a new name) is a feature of a future -version of Scala 3; so we focus on type aliases and case classes. +Opaque types (hiding a type under a new name) is a feature of Scala +3. It can be seen as a case class with a single field but without +the cost of memory allocation. Here, we will focus on type aliases +and case classes. A \textbf{type alias}\index{type alias} is an alternative name for an existing (already defined) type. We could use type aliases in our @@ -68,7 +70,7 @@ \subsection{Tuple types with names} sock: MySock = MySock(10.5,white) scala> val paid = Payment(25.0, "restaurant") -paid: Payment = Payment(25.0,restaurant) ^ +paid: Payment = Payment(25.0,restaurant) \end{lstlisting} This code defines new types named \lstinline!MySock! and \lstinline!Payment!. Values of type \lstinline!MySock! are written as \lstinline!MySock(10.5, "white")!, @@ -177,19 +179,12 @@ \subsection{Case classes with type parameters} This case class can accommodate every type \lstinline!A!. We may now create values of \lstinline!MySockX! containing a \lstinline!value! of any given type, say \lstinline!Int!: - -\begin{wrapfigure}{l}{0.48\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} scala> val s = MySockX(10.5, "white", 123) s: MySockX[Int] = MySockX(10.5,white,123) \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent Because the value \lstinline!123! has type \lstinline!Int!, -the type parameter \lstinline!A! in \lstinline!MySockX[A]! was automatically +Because the value \lstinline!123! has type \lstinline!Int!, the +type parameter \lstinline!A! in \lstinline!MySockX[A]! was automatically set to the type \lstinline!Int!. The result has type \lstinline!MySockX[Int]!. The programmer does not need to specify that type explicitly. @@ -254,18 +249,18 @@ \subsection{Tuples with one part and with zero parts} Let us compare tuples and case classes more systematically. -Parts of a case class are accessed by name with a dot syntax, for -example \lstinline!sock.color!. Parts of a tuple are accessed with -the accessors such as \lstinline!x._1!. This syntax is the same as -that for a case class whose parts have names \lstinline!_1!, \lstinline!_2!, -etc. So, it appears that tuple parts \emph{do} have names in Scala, -although those names are always automatically chosen as \lstinline!_1!, -\lstinline!_2!, etc. Tuple types are also automatically named in -Scala as \lstinline!Tuple2!, \lstinline!Tuple3!, etc., and they -are parameterized, since each part of the tuple may be of any chosen -type. A tuple type expression such as \lstinline!(Int, String)! is -just a special syntax for the parameterized type \lstinline!Tuple2[Int, String]!. -One could define the tuple types as case classes like this: +Parts of a case class are accessed with a dot syntax, for example +\lstinline!sock.color!. Parts of a tuple are accessed with the accessors +such as \lstinline!x._1!. This syntax is the same as that for a case +class whose parts have names \lstinline!_1!, \lstinline!_2!, etc. +So, it appears that tuple parts \emph{do} have names in Scala, although +those names are always automatically chosen as \lstinline!_1!, \lstinline!_2!, +etc. Tuple types are also automatically named in Scala as \lstinline!Tuple2!, +\lstinline!Tuple3!, etc., and they are parameterized, since each +part of the tuple may be of any chosen type. A tuple type expression +such as \lstinline!(Int, String)! is just a special syntax for the +parameterized type \lstinline!Tuple2[Int, String]!. One could define +the tuple types as case classes like this: \begin{lstlisting} case class Tuple2[A, B](_1: A, _2: B) case class Tuple3[A, B, C](_1: A, _2: B, _3: C) // And so on with Tuple4, Tuple5... @@ -297,8 +292,8 @@ \subsection{Tuples with one part and with zero parts} case class C() // Tuple with no parts. \end{lstlisting} -The following table summarizes the correspondence between tuples and -case classes: +The following table shows the correspondence between tuples and case +classes: \begin{center} \begin{tabular}{|c|c|} \hline @@ -321,9 +316,9 @@ \subsection{Tuples with one part and with zero parts} There are two main differences between \lstinline!case class C()! and \lstinline!case object C!: \begin{itemize} -\item A \lstinline!case object! cannot have type parameters, while we may +\item A \lstinline!case object! cannot have type parameters, while we could define a \lstinline!case class C[X, Y, Z]()! with type parameters -\lstinline!X!, \lstinline!Y!, \lstinline!Z! if needed. +\lstinline!X!, \lstinline!Y!, \lstinline!Z!, etc. \item A \lstinline!case object! is allocated in memory only once, while new values of a \lstinline!case class C()! will be allocated in memory each time \lstinline!C()! is evaluated. @@ -365,10 +360,16 @@ \subsection{Pattern matching for case classes} case BagOfSocks(MySock(size, _), _) => (size >= 10.5 && size <= 11.0) } \end{lstlisting} -In the code of this function, we match the \lstinline!bag! value -against the pattern \lstinline!BagOfSocks(MySock(size, _), _)!. This -pattern will always match and will define \lstinline!size! as a pattern -variable of type \lstinline!Double!. +In the code of this function, the value of \lstinline!bag! is matched +with the pattern expression \lstinline!BagOfSocks(MySock(size, _), _)!. +This pattern will define \lstinline!size! as a pattern variable of +type \lstinline!Double! and assign its value to the corresponding +part of the case class. For example, the value \lstinline!BagOfSocks(MySock(10.5, "white"), 6)!) +matched against \lstinline!BagOfSocks(MySock(size, _), _)! assigns +\lstinline!size! to \lstinline!10.5!. The symbol \textsf{``}\lstinline!_!\textsf{''} +means that we just ignore other parts of the case classes and do not +create any pattern variables for them (because we do not need them +in this specific code). The syntax for pattern matching for case classes is similar to the syntax for pattern matching for tuples, except for the presence of @@ -382,8 +383,8 @@ \subsection{Pattern matching for case classes} case ((size, _), _) => ... \end{lstlisting} that could be used for values of type \lstinline!((Double, String), Int)!. -We see that within pattern matching expressions, case classes behave -as tuple types with added names. +So, within pattern matching expressions, case classes behave as tuple +types with added names. Scala\textsf{'}s \textsf{``}case classes\textsf{''} got their name from their use in \lstinline!case! expressions. It is usually more convenient to use \lstinline!case! @@ -448,19 +449,18 @@ \subsection{Motivation and first examples\label{subsec:Disjunctive-Motivation-an named tuples with zero, one, two, or more parts. So, it is natural to use case classes instead of tuples: \begin{itemize} -\item \lstinline!RootsOfQ! is a value of type \lstinline!case class NoRoots()!, -or a value of type \lstinline!case class OneRoot(x: Double)!, or -a value of type \lstinline!case class TwoRoots(x: Double, y: Double)! -\item \lstinline!SearchResult! is a value of type \lstinline!case class Index(x: Int)! -or a value of type \lstinline!case class NotFound()! -\item \lstinline!Result! is a value of type \lstinline!case class Value(x: Int)! -or of type \lstinline!case class Error(message: String)! +\item \lstinline!RootsOfQ! is a value of the form \lstinline!NoRoots()!, +or of the form \lstinline!OneRoot(x: Double)!, or of the form \lstinline!TwoRoots(x: Double, y: Double)! +\item \lstinline!SearchResult! is a value of the form \lstinline!Index(x: Int)! +or of the form \lstinline!NotFound()! +\item \lstinline!Result! is a value of the form \lstinline!Value(x: Int)! +or \lstinline!Error(message: String)! \end{itemize} Our three examples are now described as types that select one case class out of a given set. It remains to see how Scala defines such types. For instance, the definition of \lstinline!RootsOfQ! needs to indicate that the case classes \lstinline!NoRoots!, \lstinline!OneRoot!, -and \lstinline!TwoRoots! are the three alternatives allowed by the +and \lstinline!TwoRoots! are the only possibilities allowed by the type \lstinline!RootsOfQ!. The Scala syntax for that definition looks like this: \begin{lstlisting} @@ -489,10 +489,10 @@ \subsection{Motivation and first examples\label{subsec:Disjunctive-Motivation-an a fixed set of case classes. This kind of type is called a \index{disjunctive type}\textbf{disjunctive }type (or a \textbf{co-product} type\index{co-product type!see \textsf{``}disjunctive type\textsf{''}}) in this book. The keywords \lstinline!final! and \lstinline!sealed! -tell the Scala compiler that the set of case classes within a disjunctive -type is fixed and unchangeable. +tell the Scala compiler that the given set of case classes within +a disjunctive type is fixed and unchangeable. -\subsection{Solved examples: Pattern matching for disjunctive types\index{solved examples}} +\subsection{Examples: Pattern matching for disjunctive types\index{examples (with code)}} Our first examples of disjunctive types are \lstinline!RootsOfQ!, \lstinline!SearchResult!, and \lstinline!Result[A]! defined in the @@ -518,10 +518,6 @@ \subsection{Solved examples: Pattern matching for disjunctive types\index{solved To use pattern matching with disjunctive types, we write \emph{several} \lstinline!case! patterns because we need to match several possible cases of the disjunctive type: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-1\baselineskip} - \begin{lstlisting} def f(r: RootsOfQ): String = r match { case NoRoots() => "no real roots" @@ -532,16 +528,13 @@ \subsection{Solved examples: Pattern matching for disjunctive types\index{solved scala> f(x) res0: String = "one real root: 2.0" \end{lstlisting} -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent Each \lstinline!case! pattern will introduce its own pattern -variables, such as \lstinline!r!, \lstinline!x!, \lstinline!y! -as in the code at left. Each pattern variable is defined only within -the \emph{local scope}\index{local scope}, that is, within the scope -of its \lstinline!case! expression. It is impossible to make a mistake -where we, say, refer to the variable \lstinline!r! within the code -that handles the case of two roots. +Each \lstinline!case! pattern will introduce its own pattern variables, +such as \lstinline!r!, \lstinline!x!, \lstinline!y! as in the code +at left. Each pattern variable is defined only within the \emph{local +scope}\index{local scope}, that is, within the scope of its \lstinline!case! +expression. It is impossible to make a mistake where we, say, refer +to the variable \lstinline!r! within the code that handles the case +of two roots. If the code only needs to work with a subset of cases, we can match all other cases with an underscore character (as in \lstinline!case _!): @@ -638,9 +631,8 @@ \subsubsection{Example \label{subsec:disj-Example-rootsofq-1}\ref{subsec:disj-Ex \end{lstlisting} This code depends on some features of Scala syntax. We can use the partial function \lstinline!{ case QEqu(b, c) => ... }! directly -as the argument of \lstinline!map! instead of defining this function -separately. This avoids having to destructure \lstinline!QEqu! at -a separate step. The \lstinline!if!/\lstinline!else! expression +as the argument of \lstinline!map!, destructuring \lstinline!QEqu! +at the same time. The \lstinline!if!/\lstinline!else! expression is replaced by an \textsf{``}embedded\textsf{''}\index{embedded if@embedded \texttt{if}} \lstinline!if! within the \lstinline!case! expression, which is easier to read. @@ -706,8 +698,8 @@ \subsubsection{Example \label{subsec:disj-Example-searchresult}\ref{subsec:disj- \end{lstlisting} In that case, the array\textsf{'}s element at the computed index will not be equal to \lstinline!goal!. We should return \lstinline!NotFound()! -in that case. The new code can be written as a \lstinline!match!/\lstinline!case! -expression for clarity: +in that case. We use a \lstinline!match!/\lstinline!case! expression +for the new logic: \begin{lstlisting} def safeBinSearch(xs: Seq[Int], goal: Int): SearchResult = binSearch(xs, goal) match { @@ -793,7 +785,8 @@ \subsubsection{Example \label{subsec:disj-Example-resultA}\ref{subsec:disj-Examp case (_, Error(m)) => Error(m) } \end{lstlisting} -We can now test the \textsf{``}safe arithmetic\textsf{''} on simple calculations: +We can now test the \textsf{``}safe arithmetic\textsf{''} on simple calculations. +Let us see what happens after an error: \begin{lstlisting} scala> add(Value(1), Value(2)) res10: Result[Int] = Value(3) @@ -801,10 +794,10 @@ \subsubsection{Example \label{subsec:disj-Example-resultA}\ref{subsec:disj-Examp scala> div(add(Value(1), Value(2)), Value(0)) res11: Result[Int] = Error(error: 3 / 0) \end{lstlisting} -We see that indeed all further computations are abandoned once an -error occurs. An error message shows only the immediate calculation -that generated the error. For instance, the error message for $20+1/0$ -never mentions $20$: +Indeed, all further computations are abandoned once an error occurs. +An error message shows only the immediate calculation that generated +the error. For instance, the error message for $20+1/0$ never mentions +$20$: \begin{lstlisting} scala> add(Value(20), div(Value(1), Value(0))) res12: Result[Int] = Error(error: 1 / 0) @@ -824,7 +817,7 @@ \subsection{Standard disjunctive types: \texttt{Option}, \texttt{Either}, \textt is a disjunctive type with two cases: the empty tuple and a one-element tuple. The names of the two case classes are \lstinline!None! and \lstinline!Some!. If the \lstinline!Option! type were not already -defined in the standard library, one could define it with the code: +defined in the Scala library, we could define it by: \begin{lstlisting} sealed trait Option[+T] // The annotation `+T` will be explained in Chapter 6. final case object None extends Option[Nothing] @@ -866,14 +859,11 @@ \subsection{Standard disjunctive types: \texttt{Option}, \texttt{Either}, \textt Typically, \lstinline!Option! is used in situations where a value may be either present or missing, especially when a missing value -is \emph{not an error}. % -\begin{comment} -The missing-value case is represented by \lstinline!None!, while -\lstinline!Some(x)! means that a value \lstinline!x! is present. -\end{comment} +\emph{is} \emph{not an error}. The missing-value case is represented +by \lstinline!None!, while \lstinline!Some(x)! represents a value +\lstinline!x! that is present. - -\subsubsection{Example \label{subsec:Disjunctive-Example-option-1}\ref{subsec:Disjunctive-Example-option-1}\index{solved examples}} +\subsubsection{Example \label{subsec:Disjunctive-Example-option-1}\ref{subsec:Disjunctive-Example-option-1}\index{examples (with code)}} Information about \textsf{``}subscribers\textsf{''} must include a name and an email address, but a telephone number is optional. To represent this information, @@ -896,7 +886,7 @@ \subsubsection{Example \label{subsec:Disjunctive-Example-option-1}\ref{subsec:Di two cases like this: \begin{lstlisting}[mathescape=true] def getDigits(phone: Option[Long]): Option[Seq[Long]] = phone match { - case None => None // Cannot obtain digits, so return `None`. + case None => None // Have no digits, so return `None`. case Some(number) => Some(digitsOf(number)) } // The function `digitsOf` was defined in Section ${\color{dkgreen}\textrm{\ref{sec:ch2Converting-a-single}}}$. \end{lstlisting} @@ -1205,7 +1195,7 @@ \subsubsection{Example \label{subsec:Disjunction-Example-Option-getOrElse}\ref{s The methods \lstinline!flatMap!, \lstinline!fold!, and \lstinline!getOrElse! are also defined for \lstinline!Either!, with the same convention -that a \lstinline!Left! value represents an error.\footnote{These methods are available in Scala 2.12 or a later version.} +that a \lstinline!Left! value represents an error.\footnote{These methods are available in Scala 2.12 and in later versions.} \paragraph{Exceptions and the \texttt{Try} type} @@ -1248,46 +1238,50 @@ \subsubsection{Example \label{subsec:Disjunction-Example-Option-getOrElse}\ref{s the error, and to continue the evaluation of the program. An \textbf{unplanned} exception\index{unplanned exception} is generated -by the Java runtime system when critical errors occur, such as an -out-of-memory error. It is rare that a programmer writes \lstinline!val y = f(x)! -while \emph{expecting} that an out-of-memory exception will sometimes -occur at that point.\footnote{Just once in the author\textsf{'}s experience, an out-of-memory exception had -to be anticipated in an Android app.} An unplanned exception indicates a serious and unforeseen problem -with memory or another critically important resource, such as the -operating system\textsf{'}s threads or file handles. Such problems usually -cannot be fixed and will prevent the program from running any further. -It is reasonable that the program should abruptly stop (or \textsf{``}crash\textsf{''} -as programmers say) after such an error. +by the Java runtime system when critical errors occur, such as a stack +overflow or an out-of-memory error. It is rare that a programmer writes +\lstinline!val y = f(x)! while \emph{expecting} that an out-of-memory +exception will likely occur at that point.\footnote{Just once in the author\textsf{'}s experience, an out-of-memory exception had +to be anticipated in an Android app as something that regularly happens +during normal usage of the app.} An unplanned exception indicates a serious problem with memory or +another critically important resource, such as the operating system\textsf{'}s +threads or file handles. Such problems usually cannot be fixed and +will prevent the program from running any further. It is reasonable +that the program should abruptly stop (or \textsf{``}crash\textsf{''} as programmers +say) after such an error. The use of planned exceptions assumes that the programmer will write code to handle each exception. This assumption makes it significantly harder to write programs correctly: it is hard to figure out and to keep in mind all the possible exceptions that a given library function -may \lstinline!throw! in its code (and in the code of all other libraries -on which it depends). Instead of using exceptions for indicating errors, -Scala programmers can write functions that return a disjunctive type, -such as \lstinline!Either!, describing both a correct result and -an error condition. Users of these functions will \emph{have} to do -pattern matching on the result values. This helps programmers to remember -and to handle all relevant error situations that the programmers anticipate -to encounter. - -However, programmers will often need to use Java or Scala libraries +may \textsf{``}\lstinline!throw!\textsf{''} in its code (and in the code of all other +libraries being used). Instead of using exceptions for indicating +errors, Scala programmers can write functions that return a disjunctive +type, such as \lstinline!Either!, describing both a correct result +and an error condition. Users of these functions will \emph{have} +to do pattern matching on the result values. This helps programmers +to avoid forgetting to handle an error situation that the code is +likely to encounter. + +Nevertheless, programmers will often need to use Java or Scala libraries that \lstinline!throw! exceptions. To help write code for these situations, -the Scala library contains a helper function called \lstinline!Try()! -and a disjunctive type, also called \lstinline!Try!. The type \lstinline!Try[A]! -is equivalent to \lstinline!Either[Throwable, A]!, where \lstinline!Throwable! -is the general type of all exceptions (i.e.~values to which a \lstinline!throw! -operation can be applied). The two parts of the disjunctive type \lstinline!Try[A]! -are called \lstinline!Failure! and \lstinline!Success[A]! (instead -of \lstinline!Left[Throwable, A]! and \lstinline!Right[Throwable, A]! -in the \lstinline!Either! type). The function \lstinline!Try(expr)! -will catch all exceptions thrown while the expression \lstinline!expr! -is evaluated. If the evaluation of \lstinline!expr! succeeds and -returns a value \lstinline!x: A!, the value of \lstinline!Try(expr)! -will be \lstinline!Success(x)!. Otherwise it will be \lstinline!Failure(t)!, -where \lstinline!t: Throwable! is the value associated with the generated -exception. Here is an example of using \lstinline!Try!: +the Scala library provides a disjunctive type called \lstinline!Try!. +The type \lstinline!Try[A]! is equivalent to \lstinline!Either[Throwable, A]!, +where \lstinline!Throwable! is the general type of all exceptions +(i.e.~values to which a \lstinline!throw! operation can be applied). +The two parts of the disjunctive type \lstinline!Try[A]! are called +\lstinline!Failure! and \lstinline!Success[A]! (instead of \lstinline!Left[Throwable, A]! +and \lstinline!Right[Throwable, A]! in the \lstinline!Either! type). +The class constructor \lstinline!Try(expr)! will catch all \textsf{``}planned\textsf{''} +exceptions thrown while the expression \lstinline!expr! is evaluated.\footnote{But \lstinline!Try()! will not catch exceptions of class \lstinline!java.lang.Error! +and its subclasses. Those exceptions are intended to represent unplanned, +serious error situations.} + +If the evaluation of \lstinline!expr! succeeds and returns a value +\lstinline!x: A!, the value of \lstinline!Try(expr)! will be \lstinline!Success(x)!. +Otherwise it will be \lstinline!Failure(t)!, where \lstinline!t: Throwable! +is the value containing the details about the exception. Here is an +example of using \lstinline!Try!: \begin{lstlisting} import scala.util.{Try, Success, Failure} @@ -1308,10 +1302,6 @@ \subsubsection{Example \label{subsec:Disjunction-Example-Option-getOrElse}\ref{s to the \lstinline!Either! type. One additional feature of \lstinline!Try! is to catch exceptions generated by the function arguments of \lstinline!map!, \lstinline!filter!, \lstinline!flatMap!, and other standard methods: - -\begin{wrapfigure}{l}{0.57\columnwidth}% -\vspace{-0.9\baselineskip} - \begin{lstlisting} scala> val y = q.map(y => throw new Exception("ouch")) y: Try[Int] = Failure(java.lang.Exception: ouch) @@ -1319,12 +1309,9 @@ \subsubsection{Example \label{subsec:Disjunction-Example-Option-getOrElse}\ref{s scala> val z = q.filter(y => throw new Exception("huh")) z: Try[Int] = Failure(java.lang.Exception: huh) \end{lstlisting} -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -\noindent In this example, the values \lstinline!y! and \lstinline!z! -were computed \emph{successfully} even though exceptions were thrown -while the function arguments of \lstinline!map! and \lstinline!filter! +In this example, the values \lstinline!y! and \lstinline!z! were +computed \emph{successfully} even though exceptions were thrown while +the function arguments of \lstinline!map! and \lstinline!filter! were evaluated. Further code can use pattern matching on the values \lstinline!y! and \lstinline!z! and examine those exceptions. However, it is important that these exceptions were caught and the program @@ -1340,19 +1327,12 @@ \subsubsection{Example \label{subsec:Disjunction-Example-Option-getOrElse}\ref{s \section{Lists and trees as recursive disjunctive types\label{sec:Lists-and-trees:recursive-disjunctive-types}} Consider this code defining a disjunctive type \lstinline!NInt!: - -\begin{wrapfigure}{l}{0.46\columnwidth}% -\vspace{-0.75\baselineskip} - \begin{lstlisting} sealed trait NInt final case class N1(x: Int) extends NInt final case class N2(n: NInt) extends NInt \end{lstlisting} -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -\noindent The type \lstinline!NInt! has two disjunctive parts, \lstinline!N1! +The type \lstinline!NInt! has two disjunctive parts: \lstinline!N1! and \lstinline!N2!. But the case class \lstinline!N2! contains a value of type \lstinline!NInt! as if the type \lstinline!NInt! were already defined. @@ -1398,12 +1378,14 @@ \subsection{\label{subsec:The-recursive-type-List}The recursive type \texttt{Lis case class for \emph{each} possible length. Instead, we define the type \lstinline!List[A]! via mathematical induction on the length of the list: -\begin{itemize} -\item Base case: empty list, \lstinline!case class List0[A]()!. -\item Inductive step: given a list of a previously defined length, say \lstinline!List!$_{n-1}$, -define a new case class \lstinline!List!$_{n}$ describing a list -with one more element of type \lstinline!A!. So we could define \lstinline!List!$_{n}=\,$\lstinline!(A, List!$_{n-1}$\lstinline!)!. -\end{itemize} + +$\bullet$ Base case: empty list, \lstinline!case class List0[A]()!. + +$\bullet$ Inductive step: given a list of a previously defined length, +say \lstinline!List!$_{n-1}$, define a new case class \lstinline!List!$_{n}$ +describing a list with one more element of type \lstinline!A!. So +we could define \lstinline!List!$_{n}=\,$\lstinline!(A, List!$_{n-1}$\lstinline!)!. + Let us try to write this inductive definition as code: \begin{lstlisting} sealed trait ListI[A] // Inductive definition of a list. @@ -1464,8 +1446,8 @@ \subsection{\label{subsec:The-recursive-type-List}The recursive type \texttt{Lis } \end{lstlisting} -The Scala library already defines the type \lstinline!List[A]! in -a different but equivalent way: +The Scala library defines the type \lstinline!List[A]! in a slightly +different way: \begin{lstlisting} sealed trait List[A] final case object Nil extends List[Nothing] @@ -1608,7 +1590,7 @@ \subsection{Tail recursion with \texttt{List}\label{subsec:Tail-recursion-with-l \lstinline!List!\textsf{'}s \lstinline!map! using low-level tricks for better performance.) -\subsubsection{Example \label{subsec:Disjunctive-Example-non-empty-list-foldLeft}\ref{subsec:Disjunctive-Example-non-empty-list-foldLeft}\index{solved examples}} +\subsubsection{Example \label{subsec:Disjunctive-Example-non-empty-list-foldLeft}\ref{subsec:Disjunctive-Example-non-empty-list-foldLeft}\index{examples (with code)}} A definition of the \textbf{non-empty list\index{non-empty list}} is similar to \lstinline!List! except that the empty-list case is @@ -1698,7 +1680,7 @@ \subsubsection{Example \label{subsec:Disjunctive-Example-non-empty-list}\ref{sub def reverse[A](xs: NEL[A]): NEL[A] = foldLeft(xs)(Last(head(xs)): NEL[A])((prev,x) => More(x, prev)) -scala> reverse(toNEL(10,List(20,30))) // The result is [30, 20, 10, 10]. +scala> reverse(toNEL(10, List(20, 30))) // The result is [30, 20, 10, 10]. res4: NEL[Int] = More(30,More(20,More(10,Last(10)))) \end{lstlisting} The last element, \lstinline!10!, should not have been repeated. @@ -1741,19 +1723,18 @@ \subsubsection{Exercise \label{subsec:Disjunctive-Exercise-map-for-NEL}\ref{subs \begin{lstlisting} def map[A,B](xs: NEL[A])(f: A => B): NEL[B] = ??? -scala> map[Int, Int](toNEL(10, List(20, 30)))(_ + 5) // The result is [15, 25, 35]. +scala> map[Int, Int](toNEL(10, List(20, 30)))(_ + 5) res7: NEL[Int] = More(15,More(25,Last(35))) \end{lstlisting} \subsubsection{Exercise \label{subsec:Disjunctive-Exercise-non-empty-list-2}\ref{subsec:Disjunctive-Exercise-non-empty-list-2}} -Implement a function \lstinline!concat! that concatenates two non-empty -lists: +Implement a function that concatenates two non-empty lists: \begin{lstlisting} -def concat[A](xs: NEL[A], ys: NEL[A]): NEL[A] = ??? +def concatNEL[A](xs: NEL[A], ys: NEL[A]): NEL[A] = ??? -scala> concat(More(1, More(2, Last(3))), More(4, Last(5))) // The result is [1, 2, 3, 4, 5]. +scala> concatNEL(More(1, More(2, Last(3))), More(4, Last(5))) res8: NEL[Int] = More(1,More(2,More(3,More(4,Last(5))))) \end{lstlisting} @@ -1767,7 +1748,7 @@ \subsection{Binary trees\label{subsec:Binary-trees}} Examples of a \textbf{binary tree\index{binary tree}} with leaves of type \lstinline!A! can be drawn as {\tiny{}}{\tiny{} \Tree[ [ [ $a_1$ ] [ $a_2$ ] ] [ $a_3$ ] ] } or as {\small{}}{\tiny{} \Tree[ [ [ $a_1$ ] [ [ $a_2$ ] [ $a_3$ ] ] ] [ [ $a_4$ ] [ $a_5$ ] ] ] }, -where $a_{i}$ are some values of type \lstinline!A!. +where $a_{1}$, ..., $a_{5}$ are some values of type \lstinline!A!. An inductive definition says that a binary tree is either a leaf with a value of type \lstinline!A! or a branch containing \emph{two} previously @@ -1781,8 +1762,8 @@ \subsection{Binary trees\label{subsec:Binary-trees}} Here are some examples of code expressions and the corresponding trees that use this definition: -\begin{wrapfigure}{l}{0.6\columnwidth}% -\vspace{-0.4\baselineskip} +\begin{wrapfigure}{l}{0.73\columnwidth}% +\vspace{-0.6\baselineskip} \begin{lstlisting} Branch(Branch(Leaf("a1"), Leaf("a2")), Leaf("a3")) \end{lstlisting} @@ -1792,17 +1773,17 @@ \subsection{Binary trees\label{subsec:Binary-trees}} \vspace{0.4\baselineskip} ~ ~{\tiny{}}{\tiny{} \Tree[ [ [ $a_1$ ] [ $a_2$ ] ] [ $a_3$ ] ] }{\tiny\par} -\begin{wrapfigure}{l}{0.6\columnwidth}% +\begin{wrapfigure}{l}{0.73\columnwidth}% \vspace{-0.4\baselineskip} \begin{lstlisting} Branch(Branch(Leaf("a1"), Branch(Leaf("a2"), Leaf("a3"))), Branch(Leaf("a4"), Leaf("a5"))) \end{lstlisting} -\vspace{1.4\baselineskip} +\vspace{1.1\baselineskip} \end{wrapfigure}% -\vspace{0.4\baselineskip} +\vspace{0.3\baselineskip} ~ ~ {\tiny{} \Tree[ [ [ $a_1$ ] [ [ $a_2$ ] [ $a_3$ ] ] ] [ [ $a_4$ ] [ $a_5$ ] ] ] } Recursive functions on trees are translated into concise code. For @@ -1836,9 +1817,9 @@ \subsection{Rose trees\label{subsec:Rose-trees}} except the branches contain a non-empty list of trees. Because of that, a rose tree can fork into arbitrarily many branches at each node, rather than always into two branches as the binary tree does. -For example, {\tiny{} \Tree[ [ [ $a_1$ ] [ $a_2$ ] [ $a_3$ ] ] [ [ $a_4$ ] [ $a_5$ ] ] ] } -and {\tiny{} \Tree[ [ $a_1$ ] [ $a_2$ ] [ $a_3$ ] [ $a_4$ ] ] } are -rose trees. +Some examples of rose trees are {\tiny{} \Tree[ [ [ $a_1$ ] [ $a_2$ ] [ $a_3$ ] ] [ [ $a_4$ ] [ $a_5$ ] [ $a_6$ ] ] ] } +and {\tiny{} \Tree[ [ $a_1$ ] [ [ $a_2$ ] [ $a_3$ ] ] [ $a_4$ ] [ $a_5$ ] ] } +. A possible definition of a data type for the rose tree is: \begin{lstlisting} @@ -1889,12 +1870,10 @@ \subsection{Perfect-shaped trees\label{subsec:Perfect-shaped-trees}} \end{lstlisting} The case \lstinline!Branch1! describes a perfect-shaped tree with total depth $1$, the case \lstinline!Branch2! has total depth $2$, -and so on. Now, we cannot rewrite this definition as a recursive type -because the case classes do not have the same structure. The non-trivial -trick is to notice that each case class \lstinline!Branch!$_{n}$ -uses the previous case class\textsf{'}s data structure with the \emph{type -parameter} set to \lstinline!(A, A)! instead of \lstinline!A!. So -we can rewrite this definition as: +and so on. The non-trivial step is to notice that each case class +\lstinline!Branch!$_{n}$ uses the previous case class\textsf{'}s data structure +with the \emph{type parameter} set to \lstinline!(A, A)! instead +of \lstinline!A!. So we can rewrite the above definition as: \begin{lstlisting} sealed trait PTree[A] final case class Leaf[A](x: A) extends PTree[A] @@ -1929,7 +1908,7 @@ \subsection{Perfect-shaped trees\label{subsec:Perfect-shaped-trees}} We see that \lstinline!PTree[A]! is a correct definition of a perfect-shaped binary tree. -\subsubsection{Example \label{subsec:Disjunctive-Example-map-regular-tree}\ref{subsec:Disjunctive-Example-map-regular-tree}\index{solved examples}} +\subsubsection{Example \label{subsec:Disjunctive-Example-map-regular-tree}\ref{subsec:Disjunctive-Example-map-regular-tree}\index{examples (with code)}} Define a (non-tail-recursive) \lstinline!map! function for a perfect-shaped binary tree. The required type signature and a test: @@ -1960,21 +1939,14 @@ \subsubsection{Example \label{subsec:Disjunctive-Example-map-regular-tree}\ref{s In the inductive step, we are given a previous tree value \lstinline!xs:PTree[(A, A)]!. It is clear that we need to apply \lstinline!map! recursively to \lstinline!xs!. Let us try: - -\begin{wrapfigure}{l}{0.61\columnwidth}% -\vspace{-0.5\baselineskip} - \begin{lstlisting} def map[A, B](t: PTree[A])(f: A => B): PTree[B] = t match { case Leaf(x) => Leaf(f(x)) case Branch(xs) => Branch(map(xs)(f)) // Type error! } \end{lstlisting} -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -\noindent Here, \lstinline!map(xs)(f)! has an incorrect type of the -function \lstinline!f!. Since \lstinline!xs! has type \lstinline!PTree[(A, A)]!, +Here, \lstinline!map(xs)(f)! has an incorrect type of the function +\lstinline!f!. Since \lstinline!xs! has type \lstinline!PTree[(A, A)]!, the recursive call \lstinline!map(xs)(f)! requires \lstinline!f! to be of type \lstinline!((A, A)) => (B, B)! instead of \lstinline!A => B!. So, we need to provide a function of the correct type instead of \lstinline!f!. @@ -2182,17 +2154,16 @@ \section{Summary} What problems can we solve now? \begin{itemize} -\item Represent values from a disjoint domain by a custom-defined disjunctive -type. +\item Represent values from a disjoint domain by a custom disjunctive type. \item Use disjunctive types instead of exceptions to indicate failures. \item Use standard disjunctive types \lstinline!Option!, \lstinline!Try!, -\lstinline!Either! and their standard methods. -\item Define recursive disjunctive types (e.g., lists and trees) and implement +\lstinline!Either! and their methods. +\item Define recursive disjunctive types (such as lists and trees) and implement recursive functions that work with them. \end{itemize} The following examples and exercises illustrate these tasks. -\subsection{Solved examples\index{solved examples}} +\subsection{Examples\index{examples (with code)}} \subsubsection{Example \label{subsec:Example-disjunctive-1}\ref{subsec:Example-disjunctive-1}} @@ -2224,8 +2195,8 @@ \subsubsection{Example \label{subsec:Example-disjunctive-1}\ref{subsec:Example-d \subsubsection{Example \label{subsec:Example-disjunctive-2}\ref{subsec:Example-disjunctive-2}} Modify \lstinline!DayOfWeek! so that the values additionally represent -a restaurant name and total amount for Fridays and a wake-up time -on Saturdays. +names of restaurants, the total amount for Fridays, and the wake-up +time on Saturdays. \subparagraph{Solution} @@ -2304,7 +2275,7 @@ \subsubsection{Example \label{subsec:Example-disjunctive-3}\ref{subsec:Example-d In Scala, the often-used code pattern \lstinline!x => x match { case ... => ... }! can be shortened to just the nameless function \lstinline!{ case ... => ... }!. -The code then becomes: +Then the code is: \begin{lstlisting} val rootAverage: RootsOfQ2 => Option[Double] = { case Linear(x) => Some(x) @@ -2385,20 +2356,13 @@ \subsubsection{Example \label{subsec:Example-disjunctive-5}\ref{subsec:Example-d \subparagraph{Solution} Begin by pattern matching on the argument: - -\begin{wrapfigure}{l}{0.625\columnwidth}% -\vspace{-0.95\baselineskip} \begin{lstlisting}[numbers=left] def f1[A, B]: Option[Either[A, B]] => Either[A, Option[B]] = { case None => ??? case Some(eab: Either[A, B]) => ??? } \end{lstlisting} - -\vspace{-1.15\baselineskip} -\end{wrapfigure}% - -\noindent In line 3, we wrote the \textbf{type annotation}\index{type annotation} +In line 3, we wrote the \textbf{type annotation}\index{type annotation} \lstinline!eab: Either[A, B]! only for clarity. It is not required here since the Scala compiler can deduce the type of the pattern variable \lstinline!eab! from the fact that we are matching a value of type @@ -2406,22 +2370,20 @@ \subsubsection{Example \label{subsec:Example-disjunctive-5}\ref{subsec:Example-d In the scope of line 2, we need to return a value of type \lstinline!Either[A, Option[B]]!. A value of that type must be either a \lstinline!Left(x)! for some -\lstinline!x:A!, or a \lstinline!Right(y)! for some \lstinline!y:Option[B]!, +\lstinline!x: A!, or a \lstinline!Right(y)! for some \lstinline!y: Option[B]!, where \lstinline!y! must be either \lstinline!None! or \lstinline!Some(z)! -with a \lstinline!z:B!. However, in our case the code is of the form -\lstinline!case None => ???!, and we cannot produce any values \lstinline!x:A! -or \lstinline!z:B! since \lstinline!A! and \lstinline!B! are arbitrary, -unknown types. The only remaining possibility is to return \lstinline!Right(y)! -with \lstinline!y = None!, and so the code must be: +with a \lstinline!z: B!. However, in our case the code is of the +form \lstinline!case None => ???!, and we cannot produce any values +\lstinline!x: A! or \lstinline!z: B! since \lstinline!A! and \lstinline!B! +are arbitrary, unknown types. The only remaining possibility is to +return \lstinline!Right(y)! with \lstinline!y = None!, and so the +code must be: \begin{lstlisting} ... case None => Right(None) // No other choice here. \end{lstlisting} In the next scope, we can perform pattern matching on the value \lstinline!eab!: - -\begin{wrapfigure}{l}{0.46\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} ... case Some(eab: Either[A, B]) = eab match { @@ -2430,21 +2392,15 @@ \subsubsection{Example \label{subsec:Example-disjunctive-5}\ref{subsec:Example-d } \end{lstlisting} -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -\noindent It remains to figure out what expressions to write in each -case. In the case \lstinline!Left(a) => ???!, we have a value of -type \lstinline!A!, and we need to compute a value of type \lstinline!Either[A, Option[B]]!. +It remains to figure out what expressions to write in each case. In +the case \lstinline!Left(a) => ???!, we have a value of type \lstinline!A!, +and we need to compute a value of type \lstinline!Either[A, Option[B]]!. We execute the same argument as before: The return value must be \lstinline!Left(x)! -for some \lstinline!x:A!, or \lstinline!Right(y)! for some \lstinline!y:Option[B]!. +for some \lstinline!x: A!, or \lstinline!Right(y)! for some \lstinline!y: Option[B]!. At this point, we have a value of type \lstinline!A! but no values of type \lstinline!B!. So we have two possibilities: to return \lstinline!Left(a)! or to return \lstinline!Right(None)!. If we decide to return \lstinline!Left(a)!, the code is: - -\begin{wrapfigure}{l}{0.655\columnwidth}% -\vspace{-0.6\baselineskip} \begin{lstlisting}[numbers=left] def f1[A, B]: Option[Either[A, B]] => Either[A, Option[B]] = { case None => Right(None) // No other choice here. @@ -2454,20 +2410,15 @@ \subsubsection{Example \label{subsec:Example-disjunctive-5}\ref{subsec:Example-d } } \end{lstlisting} - -\vspace{-0.8\baselineskip} -\end{wrapfigure}% - -\noindent Let us decide whether to return \lstinline!Left(a)! or -\lstinline!Right(None)! in line 4. Both choices will satisfy the -required return type \lstinline!Either[A, Option[B]]!. However, if -we return \lstinline!Right(None)! in that line, we will ignore the -given value \lstinline!a:A!, i.e., we will lose information.\index{information loss} +Let us decide whether to return \lstinline!Left(a)! or \lstinline!Right(None)! +in line 4. Both choices will satisfy the required return type \lstinline!Either[A, Option[B]]!. +However, if we return \lstinline!Right(None)! in that line, we will +ignore the given value \lstinline!a: A!, i.e., we will lose information.\index{information loss} So we return \lstinline!Left(a)! in line 4. -Reasoning similarly for line 5, we find that we may return \lstinline!Right(None)! +Similarly, we find in line 5 that we may return \lstinline!Right(None)! or \lstinline!Right(Some(b))!. The first choice ignores the given -value of \lstinline!b:B!. To preserve information, we make the second +value of \lstinline!b: B!. To preserve information, we make the second choice: \begin{lstlisting}[numbers=left] def f1[A, B]: Option[Either[A, B]] => Either[A, Option[B]] = { @@ -2501,21 +2452,14 @@ \subsubsection{Example \label{subsec:Example-disjunctive-6}\ref{subsec:Example-d \subparagraph{Solution} Begin by pattern matching on the argument: - -\begin{wrapfigure}{l}{0.6\columnwidth}% -\vspace{-0.6\baselineskip} \begin{lstlisting}[numbers=left] def f2[A, B]: (Option[A], Option[B]) => Option[(A, B)] = { case (Some(a), Some(b)) => ??? ... \end{lstlisting} - -\vspace{-0.8\baselineskip} -\end{wrapfigure}% - -\noindent In line 2, we have values \lstinline!a:A! and \lstinline!b:B!, -and we need to compute a value of type \lstinline!Option[(A, B)]!. -A value of that type is either \lstinline!None! or \lstinline!Some((x, y))! +In line 2, we have values \lstinline!a:A! and \lstinline!b:B!, and +we need to compute a value of type \lstinline!Option[(A, B)]!. A +value of that type is either \lstinline!None! or \lstinline!Some((x, y))! where we would need to obtain values \lstinline!x:A! and \lstinline!y:B!. Since \lstinline!A! and \lstinline!B! are arbitrary types, we cannot produce new values \lstinline!x! and \lstinline!y! from scratch. @@ -2565,9 +2509,9 @@ \subsubsection{Exercise \label{subsec:Exercise-disjunctive-1}\ref{subsec:Exercis \subsubsection{Exercise \label{subsec:Exercise-disjunctive-2}\ref{subsec:Exercise-disjunctive-2}} -In the \textsf{``}Minesweeper\textsf{''} game, count the total number of cells with -zero neighbor bombs shown by implementing a function with type signature -\lstinline!Seq[Seq[CellState]] => Int!. +In the context of the \textsf{``}Minesweeper\textsf{''} game (Exercise~\ref{subsec:Exercise-disjunctive-1}), +count the total number of cells with zero neighbor bombs shown by +implementing a function with type signature \lstinline!Seq[Seq[CellState]] => Int!. \subsubsection{Exercise \label{subsec:Exercise-disjunctive-3}\ref{subsec:Exercise-disjunctive-3}} @@ -2645,13 +2589,19 @@ \subsubsection{Exercise \label{subsec:Exercise-disjunctive-EvenList-1}\ref{subse The standard type \lstinline!List[A]! requires all its values to have the same type \lstinline!A!. Define a parameterized type \lstinline!ListX[A]! -representing a data structure in the form of a list with one or more -values where the first value has type \lstinline!A!, the second value -has type \lstinline!Option[A]!, the third \textemdash{} \lstinline!Option[Option[A]]!, +representing a data structure in the form of a non-empty list where +the first value has type \lstinline!A!, the second value has type +\lstinline!Option[A]!, the third \textemdash{} \lstinline!Option[Option[A]]!, and so on. Using a wrong type at a given place (say, \lstinline!Option[Option[A]]! instead of \lstinline!Option[A]! for the second value in the list) -should cause a type error. Implement functions \lstinline!map! and -\lstinline!foldLeft! for \lstinline!ListX!. +should cause a type error. Implement (not necessarily tail-recursive) +functions \lstinline!map! and \lstinline!foldLeft! for \lstinline!ListX!. +The type signatures: +\begin{lstlisting} +def map[A, B](lx: ListX[A])(f: A => B): ListX[B] = ??? +def foldLeft[A, R](lx: ListX[A])(init: R)(f: (R, A) = R): R = ??? +\end{lstlisting} + \section{Discussion and further developments} @@ -2694,16 +2644,16 @@ \subsection{Disjunctive types as mathematical sets} we need to distinguish the parts of the union unambiguously, even if some parts have the same type. For instance, the disjunctive type shown in Example~\ref{subsec:disj-Example-rootsofq-2} cannot be -correctly represented by the mathematical union +correctly represented by the mathematical union: \[ -\mathbb{R}^{0}\cup\mathbb{R}^{0}\cup\mathbb{R}^{1}\cup\mathbb{R}^{0}\cup\mathbb{R}^{1}\cup\mathbb{R}^{2} +\mathbb{R}^{0}\cup\mathbb{R}^{0}\cup\mathbb{R}^{1}\cup\mathbb{R}^{0}\cup\mathbb{R}^{1}\cup\mathbb{R}^{2}\quad, \] because $\mathbb{R}^{0}\cup\mathbb{R}^{0}=\mathbb{R}^{0}$ and $\mathbb{R}^{1}\cup\mathbb{R}^{1}=\mathbb{R}^{1}$, so: \[ \mathbb{R}^{0}\cup\mathbb{R}^{0}\cup\mathbb{R}^{1}\cup\mathbb{R}^{0}\cup\mathbb{R}^{1}\cup\mathbb{R}^{2}=\mathbb{R}^{0}\cup\mathbb{R}^{1}\cup\mathbb{R}^{2}\quad. \] -This representation has lost the distinction between e.g., \lstinline!Linear(x)! +For instance, this representation has no distinction between \lstinline!Linear(x)! and \lstinline!OneRootQ(x)!. In the Scala code, each part of a disjunctive type must be distinguished @@ -2730,13 +2680,13 @@ \subsection{Disjunctive types as mathematical sets} Labeled unions are not often used in mathematics, but they are needed in software engineering because real-life data is often described -by sets comprised of several disjoint parts. +by sets having several disjoint parts. \paragraph{Named \texttt{Unit} types} At first sight, it may seem strange that the zero-dimensional space is represented by a set containing \emph{one} point. Why should we -not use an empty set (rather than a set with one point) to represent +not use an empty set (rather than a set with one element) to represent the case where the equation has no real roots? The reason is that we are required to represent not only the values of the roots but also the information \emph{about} the existence of the roots. The @@ -2755,8 +2705,8 @@ \subsection{Disjunctive types as mathematical sets} one distinct value, written as \lstinline!NoRoots()!. This Scala value is fully analogous to the mathematical notation $\left(\text{\texttt{NoRoots}},u\right)_{u\in\mathbb{R}^{0}}$. -So, case classes with no parts are quite similar to \lstinline!Unit! -except for an added name, e.g., \lstinline!NoRoots()! is the \lstinline!Unit! +So, case classes with no parts are similar to \lstinline!Unit! except +for an added name, e.g., \lstinline!NoRoots()! is the \lstinline!Unit! value \lstinline!()! with name \lstinline!NoRoots!. For this reason, they can be viewed as \textsf{``}named unit\textsf{''} types.\index{unit type!named} @@ -2765,14 +2715,14 @@ \subsection{Disjunctive types in other programming languages} Disjunctive types and pattern matching turns out to be one of the defining features of FP languages. Languages that were not designed for functional programming do not support these features, while ML, -OCaml, Haskell, F\#, Scala, Swift, Elm, and PureScript support disjunctive -types and pattern matching as part of the language design. +OCaml, Haskell, F\#, Scala, Swift, Elm, PureScript, and Rust support +disjunctive types and pattern matching as part of the language design. It is remarkable that named tuple types (also called \textsf{``}structs\textsf{''} or \textsf{``}records\textsf{''}) are provided in almost every programming language, while disjunctive types are almost never present except in languages -designed for the FP paradigm. (Ada and Pascal are the only languages -that support disjunctive types without other FP features.\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(basic_instructions)\#Other_types}{https://en.wikipedia.org/wiki/Comparison\_of\_programming\_languages\_(basic\_instructions)\#Other\_types}}}) +designed for the FP paradigm.\footnote{The programming languages Ada and Pascal support disjunctive types +but no other FP features.} The \lstinline!union! types in C and C++ are not disjunctive types because it is not possible to determine which part of the union is @@ -2781,9 +2731,10 @@ \subsection{Disjunctive types in other programming languages} \begin{lstlisting}[language=C] union { int x; double y; long z; } i_d_l; \end{lstlisting} -Without a label, we (and the compiler) will not know whether a given -value of type \lstinline!i_d_l! represents an \lstinline!int!, a -\lstinline!double!, or a \lstinline!long!. This will lead to errors +This type does not include any label telling us which of the values +is present. Without a label, we (and the compiler) will not know whether +a given value of type \lstinline!i_d_l! represents an \lstinline!int!, +a \lstinline!double!, or a \lstinline!long!. This will lead to errors that are hard to detect. Programming languages of the C family (C, C++, Objective C, Java) @@ -2802,20 +2753,24 @@ \subsection{Disjunctive types in other programming languages} final case class GREEN() extends Color final case class BLUE() extends Color \end{lstlisting} -If we \textsf{``}enrich\textsf{''} the \lstinline!enum! types with extra data, so -that the tuples could be non-empty, and extend the \lstinline!switch! -expression to be able to handle the extra data, we will obtain the -full functionality of disjunctive types. A definition of \lstinline!RootsOfQ! -could then look like this: +If we add extra data to the \lstinline!enum! types, allowing the +tuples to be non-empty, and extend the \lstinline!switch! expression +to be able to handle the extra data, we will recover the full functionality +of disjunctive types. A definition of \lstinline!RootsOfQ! could +then look like this: \begin{lstlisting} enum RootsOfQ { // This is not valid in Java! NoRoots(), OneRoot(Double x), TwoRoots(Double x, Double y); } \end{lstlisting} -A future version of Scala 3 will have a syntax for disjunctive types\footnote{\texttt{\href{https://dotty.epfl.ch/docs/reference/enums/adts.html}{https://dotty.epfl.ch/docs/reference/enums/adts.html}}} -that resembles \textsf{``}enriched \lstinline!enum!\textsf{''}: +Scala 3 has a shorter a syntax for disjunctive types\footnote{\texttt{\href{https://dotty.epfl.ch/docs/reference/enums/adts.html}{https://dotty.epfl.ch/docs/reference/enums/adts.html}}} +that resembles Java\textsf{'}s \textsf{``}\lstinline!enum!\textsf{''}: \begin{lstlisting} -enum RootsOfQ { case NoRoots; case OneRoot(x: Double); case TwoRoots(x: Double, y: Double) } +enum RootsOfQ { + case NoRoots + case OneRoot(x: Double) + case TwoRoots(x: Double, y: Double) +} \end{lstlisting} For comparison, the syntax for a disjunctive type equivalent to \lstinline!RootsOfQ! in OCaml and Haskell is: @@ -2830,7 +2785,7 @@ \subsection{Disjunctive types in other programming languages} This is more concise than the Scala syntax. When reasoning about disjunctive types, it is inconvenient to write out long type definitions. Chapter~\ref{chap:5-Curry-Howard} will introduce a mathematical notation designed for efficient reasoning -about types. That notation is still more concise than Haskell or OCaml. +about types. That notation is even more concise than Haskell or OCaml. \subsection{Disjunctions and conjunctions in formal logic\label{subsec:Disjunctions-and-conjunctions}} @@ -2881,9 +2836,9 @@ \subsection{Disjunctions and conjunctions in formal logic\label{subsec:Disjuncti We find that tuples are related to logical conjunctions in the same way as disjunctive types are related to logical disjunctions. This -is the main reason for choosing the name \textsf{``}disjunctive types\textsf{''}.\footnote{Disjunctive types are also known as \textsf{``}variants\textsf{''}, \textsf{``}sum types\textsf{''}, -\textsf{``}co-product types\textsf{''}, and \textsf{``}tagged union types\textsf{''}. This book calls -them \textsf{``}disjunctive types\textsf{''} and \textsf{``}co-product types\textsf{''} interchangeably.} +is the main reason for choosing the name \textsf{``}disjunctive types\textsf{''}.\footnote{Disjunctive types are also called variants, sum types, co-product +types, and tagged unions. This book uses the terms \textsf{``}disjunctive +types\textsf{''} and \textsf{``}co-product types\textsf{''} interchangeably.} The correspondence between disjunctions, conjunctions, and data types is explained in more detail in Chapter~\ref{chap:5-Curry-Howard}. diff --git a/sofp-src/sofp-essay1.lyx b/sofp-src/sofp-essay1.lyx new file mode 100644 index 000000000..53ef38325 --- /dev/null +++ b/sofp-src/sofp-essay1.lyx @@ -0,0 +1,1287 @@ +#LyX 2.3 created this file. For more info see http://www.lyx.org/ +\lyxformat 544 +\begin_document +\begin_header +\save_transient_properties true +\origin unavailable +\textclass scrbook +\begin_preamble +\usepackage[all]{xy} % xypic + +% pstricks with support for pdflatex +%\usepackage{pdftricks} +%\begin{psinputs} +% \usepackage{pstricks} +% \usepackage{multido} +%\end{psinputs} +\usepackage{pstricks} + +% Cover picture on first page. +\usepackage{wallpaper} +% Custom commands for cover page. +\usepackage[absolute,overlay]{textpos} + +% No page numbers on "Part" pages. +\renewcommand*{\partpagestyle}{empty} + +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + +% Running head: works, but results are not satisfactory. +%\usepackage{scrlayer-scrpage} +%\automark[subsection]{chapter} + + +% "Better text justification"? Actually, this causes a fatal error "auto expansion is only possible with scalable fonts". +%\usepackage{microtype} + +% Fix the numbering of exercises: subsubsections appear as paragraphs but are numbered. +%\usepackage{titlesec} % Incompatible with komascript's later versions. +% See https://tex.stackexchange.com/questions/7627/how-to-reference-paragraph +% See the `titlesec` package documentation at http://www.ctex.org/documents/packages/layout/titlesec.pdf +%\titleformat{\subsubsection}[runin]{\normalfont\normalsize\bfseries}{}{0pt}{} +%\titlespacing{\subsubsection}{0pt}{5pt}{3\wordsep} +%\titleformat{\subparagraph}[runin]{\normalfont\normalsize\bfseries}{}{0pt}{} +%\titlespacing{\subparagraph}{\parindent}{\parskip}{3\wordsep} +%\titlespacing{\paragraph}{0pt}{3pt}{2\wordsep} + +\renewcommand*{\subsubsectionformat}{} +\RedeclareSectionCommand[ % Statement 1.2.3.4 + runin=true, + afterskip=2ex, + beforeskip=2.5pt plus 0.3pt minus 0.05pt, + afterindent=false, + font={\normalfont\normalsize\bfseries} +]{subsubsection} +\RedeclareSectionCommand[ % Proof + runin=true, + font={\normalfont\normalsize\bfseries}, + afterindent=false, + afterskip=2ex, + beforeskip=0pt +]{subparagraph} +\RedeclareSectionCommand[ + runin=true, + font={\normalfont\normalsize\bfseries}, + afterskip=1.3ex, + beforeskip=0pt +]{paragraph} + +% Make page headers and page numbers smaller +\addtokomafont{pagehead}{\small} +\addtokomafont{pagenumber}{\small} + +% Double-stroked fonts to replace the non-working \mathbb{1}. +\usepackage{bbold} +\DeclareMathAlphabet{\bbnumcustom}{U}{BOONDOX-ds}{m}{n} % Use BOONDOX-ds or bbold. +\newcommand{\custombb}[1]{\bbnumcustom{#1}} +% The LyX document will define a macro \bbnum{#1} that calls \custombb{#1}. + +% Scala syntax highlighting. See https://tex.stackexchange.com/questions/202479/unable-to-define-scala-language-with-listings +%\usepackage[T1]{fontenc} +%\usepackage[utf8]{inputenc} +%\usepackage{beramono} +%\usepackage{listings} +% The listing settings are now supported by LyX in a separate section "Listings". +\usepackage{xcolor} + +\definecolor{scalakeyword}{rgb}{0.16,0.07,0.5} +\definecolor{dkgreen}{rgb}{0,0.6,0} +\definecolor{gray}{rgb}{0.5,0.5,0.5} +\definecolor{mauve}{rgb}{0.58,0,0.82} +\definecolor{aqua}{rgb}{0.9,0.96,0.999} +\definecolor{scalatype}{rgb}{0.2,0.3,0.2} +\definecolor{teal}{rgb}{0,0.6,0} + +% These settings are now in the Listings tab in LyX. +%\lstdefinestyle{myScalastyle}{ +% language=scala, % This should be defined first!!! Otherwise it overrides all customization via morekeywords / otherkeywords. +% otherkeywords={{=,=>,<-,<\%,<:,>:,\#,@,*,+,-,/,::,:,[,]}}, +% frame=tb, +% aboveskip=2mm, +% belowskip=2mm, +% showstringspaces=false, +% columns=flexible, +% basicstyle={\small\ttfamily}, +% extendedchars=true, +% %numbers=none, +% numberstyle=\tiny\color{gray}, +% keywordstyle=\color{blue}, +% commentstyle=\color{dkgreen}, +% stringstyle=\color{mauve}, +% frame=single, +% framerule=0.01mm, +% breaklines=true, +% breakatwhitespace=true, +% tabsize=3, +% framexleftmargin=4mm, framexrightmargin=4mm, +% xleftmargin=4mm, xrightmargin=4mm, % Making these margins the same has a good effect. +% framextopmargin=0.5mm, framexbottommargin=.5mm, +% fillcolor=\color{aqua}, +% rulecolor=\color{aqua}, +% rulesepcolor=\color{aqua}, +% backgroundcolor=\color{aqua}, +% mathescape=true, +%} + +% Example usage: \begin{lstlisting}[style=myScalastyle] object blah \end{lstlisting} +%\newenvironment{scala}{\begin{lstlisting}[style=myScalastyle]}{\end{lstlisting}} +%\lstnewenvironment{scala}{\lstset{style=myScalastyle}}{} + +\usepackage[nocenter]{qtree} % simple tree drawing +\usepackage{relsize} % make math symbols larger or smaller; supports \smaller etc. +\usepackage{stmaryrd} % some extra symbols such as \fatsemi +% Note: using \forwardcompose inside a \text{} will cause a LaTeX error! +\newcommand{\forwardcompose}{\hspace{1.2pt}\ensuremath\mathsmaller{\fatsemi}\hspace{1.5pt}} +% this is ugly, I used this before I found \fatsemi: +%\newcommand{\bef}{\hspace{1.0pt}\ensuremath\raisebox{2pt}{$\mathsmaller{\mathsmaller{\circ}}$}\hspace{-2.9pt},} +%\makeatletter +% Macros to assist LyX with XYpic when using scaling. +\newcommand{\xyScaleX}[1]{% +\makeatletter +\xydef@\xymatrixcolsep@{#1} +\makeatother +} % end of \xyScaleX +\makeatletter +\newcommand{\xyScaleY}[1]{% +\makeatletter +\xydef@\xymatrixrowsep@{#1} +\makeatother +} % end of \xyScaleY + +% Increase the default vertical space inside table cells. +\renewcommand\arraystretch{1.4} + +% Color for PDF hyperlinks. +\definecolor{hlink}{rgb}{0.06, 0.14, 0.48} + +% Make underline green. +\definecolor{greenunder}{rgb}{0.1,0.6,0.2} +%\newcommand{\munderline}[1]{{\color{greenunder}\underline{{\color{black}#1}}\color{black}}} +\def\mathunderline#1#2{\color{#1}\underline{{\color{black}#2}}\color{black}} +% The LyX document will define a macro \gunderline{#1} that will use \mathunderline with the color `greenunder`. +%\def\gunderline#1{\mathunderline{greenunder}{#1}} % This is now defined by LyX itself with GUI support. + + +% Prepare settings for imposing a color background for all displayed math. This will be done by a script later. +\usepackage{empheq} % Background on all displayed equations. +\definecolor{mathbg}{rgb}{1.0, .98, .87} +\newcommand*\mymathbgbox[1]{% +\setlength{\fboxsep}{0pt}% +\colorbox{mathbg}{\hspace{0.5mm}#1\hspace{0.5mm}}} +%\renewenvironment{align}{% +%\begin{empheq}[box=\mymathbgbox]{align}}{% +%\endalign\end{empheq}} +% Run a command such as LC_ALL=C sed -i bak -e 's|\\begin{align}|\\begin{empheq}[box=\\mymathbgbox]{align}|; s|\\end{align}|\\end{empheq}|' sofp-filterable.tex +% This is not used now because the results are not great. + +% Better text quotes. +\renewcommand\textquotedblleft{``} +\renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} +\end_preamble +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt +\use_default_options true +\master sofp.lyx +\maintain_unincluded_children false +\language english +\language_package default +\inputencoding auto +\fontencoding global +\font_roman "palatino" "default" +\font_sans "helvet" "default" +\font_typewriter "cmtt" "default" +\font_math "newtxmath" "auto" +\font_default_family default +\use_non_tex_fonts false +\font_sc false +\font_osf false +\font_sf_scale 100 100 +\font_tt_scale 100 100 +\use_microtype false +\use_dash_ligatures false +\graphics default +\default_output_format default +\output_sync 0 +\bibtex_command default +\index_command makeindex +\paperfontsize 10 +\spacing single +\use_hyperref true +\pdf_title "The Science of Functional Programming: A Tutorial, with Examples in Scala" +\pdf_author "Sergei Winitzki" +\pdf_subject "Functional programming" +\pdf_keywords "Functional programming, Scala, Type theory, Category theory, Formal logic, Programming languages" +\pdf_bookmarks true +\pdf_bookmarksnumbered true +\pdf_bookmarksopen true +\pdf_bookmarksopenlevel 2 +\pdf_breaklinks true +\pdf_pdfborder true +\pdf_colorlinks true +\pdf_backref page +\pdf_pdfusetitle true +\pdf_quoted_options "linkcolor=hlink" +\papersize custom +\use_geometry true +\use_package amsmath 1 +\use_package amssymb 1 +\use_package cancel 1 +\use_package esint 1 +\use_package mathdots 1 +\use_package mathtools 1 +\use_package mhchem 1 +\use_package stackrel 1 +\use_package stmaryrd 1 +\use_package undertilde 1 +\cite_engine basic +\cite_engine_type default +\biblio_style plain +\use_bibtopic false +\use_indices false +\paperorientation portrait +\suppress_date false +\justification false +\use_refstyle 1 +\use_minted 0 +\index Index +\shortcut idx +\color #008000 +\end_index +\paperwidth 7.444in +\paperheight 9.68in +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm +\secnumdepth 3 +\tocdepth 2 +\paragraph_separation indent +\paragraph_indentation default +\is_math_indent 0 +\math_numbering_side default +\quotes_style english +\dynamic_quotes 0 +\papercolumns 1 +\papersides 2 +\paperpagestyle default +\listings_params "language=Scala,morekeywords={{scala}},otherkeywords={=,=>,<-,<\%,<:,>:,\#,@,:,[,],.,???},keywordstyle={\color{scalakeyword}},morekeywords={[2]{String,Short,Int,Long,Char,Boolean,Double,Float,BigDecimal,Seq,Map,Set,Option,Either,Future,Successful,LazyList,Vector,Range,IndexedSeq,true,false,None,List,Nil,Try,Success,Failure,Some,Left,Right,Nothing,Any,Array,Unit,Iterator,Stream,Throwable,Integer,Object}},keywordstyle={[2]{\color{scalatype}}},frame=tb,aboveskip={1.5mm},belowskip={0.5mm},showstringspaces=false,columns=fullflexible,keepspaces=true,basicstyle={\smaller\ttfamily},extendedchars=true,numbers=none,numberstyle={\tiny\color{gray}},commentstyle={\color{dkgreen}},stringstyle={\color{mauve}},frame=single,framerule={0.0mm},breaklines=true,breakatwhitespace=true,tabsize=3,framexleftmargin={0.5mm},framexrightmargin={0.5mm},xleftmargin={1.5mm},xrightmargin={1.5mm},framextopmargin={0.5mm},framexbottommargin={0.5mm},fillcolor={\color{aqua}},rulecolor={\color{aqua}},rulesepcolor={\color{aqua}},backgroundcolor={\color{aqua}},mathescape=false,extendedchars=true" +\tracking_changes false +\output_changes false +\html_math_output 0 +\html_css_as_file 0 +\html_be_strict false +\end_header + +\begin_body + +\begin_layout Addchap +Essay: Towards functional data engineering with Scala +\end_layout + +\begin_layout Standard +Data engineering is among the highest-demand +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://archive.is/mK59h" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + novel occupations in the IT world today. + Data engineers create software pipelines that process large volumes of + data efficiently. + Why did the Scala programming language emerge as a premier tool +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.slideshare.net/noootsab/scala-the-unpredicted-lingua-franca-for-data-science" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + for crafting the foundational data engineering technologies such as Spark + or Akka? Why is Scala in high demand +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://techcrunch.com/2016/06/14/scala-is-the-new-golden-child/" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + within the world of big data? +\end_layout + +\begin_layout Standard +There are reasons to believe that the choice of Scala was not accidental. +\end_layout + +\begin_layout Addsec +Data is math +\end_layout + +\begin_layout Standard +Humanity has been working with data at least since Babylonian tax tables +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.nytimes.com/2017/08/29/science/trigonometry-babylonian-tablet.html" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + and the ancient Chinese number books. +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +name "https://quatr.us/china/science/chinamath.htm" +target "https://web.archive.org/web/20170425233550/https://quatr.us/china/science/chinamath.htm" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + Mathematics summarizes several millennia's worth of data processing experience + in a few fundamental tenets: +\end_layout + +\begin_layout Standard +\begin_inset Float figure +wide false +sideways false +status open + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename type-error.jpg + width 40line% + +\end_inset + + +\begin_inset VSpace -50baselineskip% +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset Index idx +status open + +\begin_layout Plain Layout +jokes +\end_layout + +\end_inset + +Mixing incompatible data types produces nonsensical results. +\begin_inset CommandInset label +LatexCommand label +name "fig:A-nonsensical-calculation-1" + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Itemize +Data is +\emph on +immutable +\emph default + (because true facts are immutable). +\end_layout + +\begin_layout Itemize +Values of different +\emph on +type +\emph default + (population count, land area, distance, price, location, time, growth percentag +e, etc.) need to be handled separately. + For example, it is an error to add a distance to a population count. +\end_layout + +\begin_layout Itemize +Data processing should be performed according to +\emph on +mathematical formulas +\emph default +. + True mathematical formulas are immutable and always give the same results + from the same input data. +\end_layout + +\begin_layout Standard +Violating these tenets produces nonsense (see Fig. +\begin_inset space \space{} +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:A-nonsensical-calculation-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + for a real-life illustration). +\end_layout + +\begin_layout Standard +The power of the principles of mathematics extends over all epochs and all + cultures; math is the same in San Francisco, in Rio de Janeiro, in Kuala-Lumpur +, and in Pyongyang (Fig. +\begin_inset space \space{} +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:The-Pyongyang-method-of-error-free-programming-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). +\end_layout + +\begin_layout Addsec +Functional programming is math +\end_layout + +\begin_layout Standard +The functional programming paradigm is based on mathematical principles: + values are immutable, data processing is coded through formula-like expressions +, and each type of data is required to match correctly during the computations. + The type-checking process automatically prevents programmers from making + many kinds of coding errors. + In addition, programming languages such as Scala and Haskell have a set + of features adapted to building powerful abstractions and domain-specific + languages. + This power of abstraction is not accidental. + Since mathematics is the ultimate art of building abstractions, math-based + functional programming languages capitalize on having several millennia + of mathematical experience. +\end_layout + +\begin_layout Standard +A prominent example of how mathematics informs the design of programming + languages is the connection between constructive logic +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://en.wikipedia.org/wiki/Intuitionistic_logic" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + and the programming language's type system, called the Curry-Howard (CH) + correspondence. + The main idea of the CH correspondence +\begin_inset Index idx +status open + +\begin_layout Plain Layout +Curry-Howard correspondence +\end_layout + +\end_inset + + is to think of programs as mathematical formulas that compute a value of + a certain type +\begin_inset Formula $A$ +\end_inset + +. + The CH correspondence is between programs and logical propositions: To + any program that computes a value of type +\begin_inset Formula $A$ +\end_inset + +, there corresponds a proposition stating that +\begin_inset Quotes eld +\end_inset + +a value of type +\begin_inset Formula $A$ +\end_inset + + can be computed +\begin_inset Quotes erd +\end_inset + +. +\end_layout + +\begin_layout Standard +This may sound rather theoretical so far. + To see the real value of the CH correspondence, recall that formal logic + has operations +\begin_inset Quotes eld +\end_inset + + +\series bold +\emph on +and +\series default +\emph default + +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + + +\series bold +\emph on +or +\series default +\emph default + +\begin_inset Quotes erd +\end_inset + +, and +\begin_inset Quotes eld +\end_inset + + +\series bold +\emph on +implies +\series default +\emph default + +\begin_inset Quotes erd +\end_inset + +. + For any two propositions +\begin_inset Formula $A$ +\end_inset + +, +\begin_inset Formula $B$ +\end_inset + +, we can construct the propositions +\begin_inset Quotes eld +\end_inset + + +\begin_inset Formula $A$ +\end_inset + + +\series bold +\emph on +and +\series default +\emph default + +\begin_inset Formula $B$ +\end_inset + + +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + + +\begin_inset Formula $A$ +\end_inset + + +\series bold +\emph on +or +\series default +\emph default + +\begin_inset Formula $B$ +\end_inset + + +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + + +\begin_inset Formula $A$ +\end_inset + + +\series bold +\emph on +implies +\series default +\emph default + +\begin_inset Formula $B$ +\end_inset + + +\begin_inset Quotes erd +\end_inset + +. + These three logical operations are foundational; without one of them, the + logic is +\emph on +incomplete +\emph default + (you cannot derive some theorems). +\end_layout + +\begin_layout Standard +A programming language +\series bold +obeys the CH correspondence +\series default + +\begin_inset Index idx +status open + +\begin_layout Plain Layout +Curry-Howard correspondence +\end_layout + +\end_inset + + with the logic if for any types +\begin_inset Formula $A$ +\end_inset + +, +\begin_inset Formula $B$ +\end_inset + +, the language also contains composite types corresponding to the logical + formulas +\begin_inset Quotes eld +\end_inset + + +\begin_inset Formula $A$ +\end_inset + + +\series bold +\emph on +or +\series default +\emph default + +\begin_inset Formula $B$ +\end_inset + + +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + + +\begin_inset Formula $A$ +\end_inset + + +\series bold +\emph on +and +\series default +\emph default + +\begin_inset Formula $B$ +\end_inset + + +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + + +\begin_inset Formula $A$ +\end_inset + + +\series bold +\emph on +implies +\series default +\emph default + +\begin_inset Formula $B$ +\end_inset + + +\begin_inset Quotes erd +\end_inset + +. + In Scala, these composite types are +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Either[A, B] +\end_layout + +\end_inset + +, the tuple +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +(A,B) +\end_layout + +\end_inset + +, and the function type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +A => B +\end_layout + +\end_inset + +. + All modern functional languages such as OCaml, Haskell, Scala, F#, Swift, + Elm, and PureScript support these three type constructions and thus obey + the CH correspondence. + Having a +\emph on +complete +\emph default + logic in a language's type system enables declarative domain-driven code + design. +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://fsharpforfunandprofit.com/ddd/" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Wrap figure +lines 0 +placement I +overhang 0in +width "50col%" +status open + +\begin_layout Plain Layout +\align center +\begin_inset VSpace -50baselineskip% +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset VSpace -300baselineskip% +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset Float figure +wide false +sideways false +status open + +\begin_layout Plain Layout + +\end_layout + +\begin_layout Plain Layout +\align center +\begin_inset Graphics + filename no-bugs.jpg + width 50line% + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset Caption Standard + +\begin_layout Plain Layout +\begin_inset Index idx +status open + +\begin_layout Plain Layout +jokes +\end_layout + +\end_inset + +The Pyongyang method of error-free software engineering. +\begin_inset CommandInset label +LatexCommand label +name "fig:The-Pyongyang-method-of-error-free-programming-1" + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +It is interesting to note that most older programming languages (C/C++, + Java, JavaScript, Python) do not support some of these composite types. + In other words, these programming languages have type systems based on + an incomplete logic. + As a result, users of these languages have to implement burdensome workarounds + that make for error-prone code. + Failure to follow mathematical principles has real costs (Figure +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:The-Pyongyang-method-of-error-free-programming-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). +\end_layout + +\begin_layout Addsec +The power of abstraction +\end_layout + +\begin_layout Standard +Early adopters of Scala, such as Netflix, LinkedIn, and Twitter, were implementi +ng what is now called +\begin_inset Quotes eld +\end_inset + +big data engineering +\begin_inset Quotes erd +\end_inset + +. + The required software needs to be highly concurrent, distributed, and resilient + to failure. + Those software companies used Scala as their main implementation language + and reaped the benefits of functional programming. +\end_layout + +\begin_layout Standard +What makes Scala suitable for big data tasks? The only reliable way of managing + massively concurrent code is to use sufficiently high-level abstractions + that make application code declarative. + The two most important such abstractions are the +\begin_inset Quotes eld +\end_inset + +resilient distributed dataset +\begin_inset Quotes erd +\end_inset + + (RDD) of Apache Spark and the +\begin_inset Quotes eld +\end_inset + +reactive stream +\begin_inset Quotes erd +\end_inset + + used in systems such as Kafka, Akka Streams, and Apache Flink. + While these abstractions are certainly implementable in Java or Python, + a fully declarative and type-safe usage is possible only in a programming + language with a sophisticated type system. + Among the currently available mature functional languages, only Scala and + Haskell are technically adequate for that task, due to their support for + typeclasses and higher-order types. + The early adopters of Scala were able to benefit from the powerful abstractions + Scala supports. + In this way, Scala enabled those businesses to engineer and to scale up + their massively concurrent computations. +\end_layout + +\begin_layout Standard +It remains to see why Scala (and not, say, OCaml or Haskell) became the + +\emph on +lingua franca +\emph default + of big data. +\end_layout + +\begin_layout Addsec +Scala is Java on math +\end_layout + +\begin_layout Standard +The recently invented general-purpose functional programming languages may + be divided into +\begin_inset Quotes eld +\end_inset + +academic +\begin_inset Quotes erd +\end_inset + + (OCaml, Haskell) and +\begin_inset Quotes eld +\end_inset + +industrial +\begin_inset Quotes erd +\end_inset + + (F#, Scala, Swift). +\end_layout + +\begin_layout Standard +The +\begin_inset Quotes eld +\end_inset + +academic +\begin_inset Quotes erd +\end_inset + + languages are clean-room implementations of well-researched mathematical + principles of programming language design (the CH correspondence being + one such principle). + These languages are not limited by requirements of compatibility with any + existing platforms or libraries. + Because of this, the +\begin_inset Quotes eld +\end_inset + +academic +\begin_inset Quotes erd +\end_inset + + languages have been designed and used for pursuing various mathematical + ideas to their logical conclusion. +\begin_inset Foot +status open + +\begin_layout Plain Layout +OCaml has arbitrary recursive product and co-product types that can be freely + combined with object-oriented types. + Haskell removes all side effects from the language and supports partial + type functions of arbitrarily high order. +\end_layout + +\end_inset + + At the same time, software practitioners struggle to adopt these programming + languages due to a steep learning curve, a lack of enterprise-grade libraries + and tool support, and immature package management. +\end_layout + +\begin_layout Standard +The languages from the +\begin_inset Quotes eld +\end_inset + +industrial +\begin_inset Quotes erd +\end_inset + + group are based on existing and mature software ecosystems: F# on .NET, + Scala on JVM, and Swift on the MacOS/iOS platform. + One of the important design requirements for these languages is 100% binary + compatibility with their +\begin_inset Quotes eld +\end_inset + +parent +\begin_inset Quotes erd +\end_inset + + platforms and languages (F# with C#, Scala with Java, and Swift with Objective- +C). + Because of this, developers can immediately take advantage of the existing + tooling, package management, and industry-strength libraries, while slowly + ramping up the idiomatic usage of new language features. + However, the same compatibility requirements dictate certain limitations + in the languages, making their design less than fully satisfactory from + the functional programming viewpoint. +\end_layout + +\begin_layout Standard +It is now easy to see why the adoption rate of the +\begin_inset Quotes eld +\end_inset + +industrial +\begin_inset Quotes erd +\end_inset + + group of languages is much higher +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.tiobe.com/tiobe-index/" +literal "false" + +\end_inset + + +\family default +, archived in 2019 at +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "http://archive.is/RsNH8" + +\end_inset + + +\end_layout + +\end_inset + + than that of the +\begin_inset Quotes eld +\end_inset + +academic +\begin_inset Quotes erd +\end_inset + + languages. + The transition to the functional paradigm is also smoother for software + developers because F#, Scala, and Swift seamlessly support the familiar + object-oriented programming +\begin_inset Index idx +status open + +\begin_layout Plain Layout +object-oriented programming +\end_layout + +\end_inset + + paradigm. + At the same time, these new languages still have logically complete type + systems, which gives developers an important benefit of type-safe domain + modeling. +\end_layout + +\begin_layout Standard +Nevertheless, the type systems of these languages are not equally powerful. + For instance, F# and Swift are similar to OCaml in many ways but omit OCaml's + parameterized modules and some other features. + Of all mentioned languages, only Scala and Haskell directly support typeclasses + and higher-order functions on types, which are helpful for expressing abstracti +ons such as automatically parallelized data sets or asynchronous data streams. +\end_layout + +\begin_layout Standard +To see the impact of these advanced features, consider LINQ, a domain-specific + language for database queries on .NET, implemented in C# and F# through + a special built-in syntax supported by Microsoft's compilers. + Analogous functionality is provided in Scala as a +\emph on +library +\emph default +, without need to modify the Scala compiler, by several open-source projects + such as Slick and Quill. + Similar libraries exist for Haskell — but not in languages with less powerful + type systems. +\end_layout + +\begin_layout Addsec +Summary +\end_layout + +\begin_layout Standard +Only Scala has all of the features required for industrial-grade functional + programming: +\end_layout + +\begin_layout Enumerate +Functional collections in the standard library. +\end_layout + +\begin_layout Enumerate +A sophisticated type system with support for typeclasses and higher-order + types. +\end_layout + +\begin_layout Enumerate +Seamless compatibility with a mature software ecosystem (JVM). +\end_layout + +\begin_layout Standard +Based on this assessment, it appears that Scala is a good choice of an implement +ation language for big data engineering. +\end_layout + +\end_body +\end_document diff --git a/sofp-src/sofp-essay1.pre.md b/sofp-src/sofp-essay1.pre.md new file mode 100644 index 000000000..e69de29bb diff --git a/sofp-src/sofp-essay2.lyx b/sofp-src/sofp-essay2.lyx new file mode 100644 index 000000000..863bd14e1 --- /dev/null +++ b/sofp-src/sofp-essay2.lyx @@ -0,0 +1,2443 @@ +#LyX 2.3 created this file. For more info see http://www.lyx.org/ +\lyxformat 544 +\begin_document +\begin_header +\save_transient_properties true +\origin unavailable +\textclass scrbook +\begin_preamble +\usepackage[all]{xy} % xypic + +% pstricks with support for pdflatex +%\usepackage{pdftricks} +%\begin{psinputs} +% \usepackage{pstricks} +% \usepackage{multido} +%\end{psinputs} +\usepackage{pstricks} + +% Cover picture on first page. +\usepackage{wallpaper} +% Custom commands for cover page. +\usepackage[absolute,overlay]{textpos} + +% No page numbers on "Part" pages. +\renewcommand*{\partpagestyle}{empty} + +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + +% Running head: works, but results are not satisfactory. +%\usepackage{scrlayer-scrpage} +%\automark[subsection]{chapter} + + +% "Better text justification"? Actually, this causes a fatal error "auto expansion is only possible with scalable fonts". +%\usepackage{microtype} + +% Fix the numbering of exercises: subsubsections appear as paragraphs but are numbered. +%\usepackage{titlesec} % Incompatible with komascript's later versions. +% See https://tex.stackexchange.com/questions/7627/how-to-reference-paragraph +% See the `titlesec` package documentation at http://www.ctex.org/documents/packages/layout/titlesec.pdf +%\titleformat{\subsubsection}[runin]{\normalfont\normalsize\bfseries}{}{0pt}{} +%\titlespacing{\subsubsection}{0pt}{5pt}{3\wordsep} +%\titleformat{\subparagraph}[runin]{\normalfont\normalsize\bfseries}{}{0pt}{} +%\titlespacing{\subparagraph}{\parindent}{\parskip}{3\wordsep} +%\titlespacing{\paragraph}{0pt}{3pt}{2\wordsep} + +\renewcommand*{\subsubsectionformat}{} +\RedeclareSectionCommand[ % Statement 1.2.3.4 + runin=true, + afterskip=2ex, + beforeskip=2.5pt plus 0.3pt minus 0.05pt, + afterindent=false, + font={\normalfont\normalsize\bfseries} +]{subsubsection} +\RedeclareSectionCommand[ % Proof + runin=true, + font={\normalfont\normalsize\bfseries}, + afterindent=false, + afterskip=2ex, + beforeskip=0pt +]{subparagraph} +\RedeclareSectionCommand[ + runin=true, + font={\normalfont\normalsize\bfseries}, + afterskip=1.3ex, + beforeskip=0pt +]{paragraph} + +% Make page headers and page numbers smaller +\addtokomafont{pagehead}{\small} +\addtokomafont{pagenumber}{\small} + +% Double-stroked fonts to replace the non-working \mathbb{1}. +\usepackage{bbold} +\DeclareMathAlphabet{\bbnumcustom}{U}{BOONDOX-ds}{m}{n} % Use BOONDOX-ds or bbold. +\newcommand{\custombb}[1]{\bbnumcustom{#1}} +% The LyX document will define a macro \bbnum{#1} that calls \custombb{#1}. + +% Scala syntax highlighting. See https://tex.stackexchange.com/questions/202479/unable-to-define-scala-language-with-listings +%\usepackage[T1]{fontenc} +%\usepackage[utf8]{inputenc} +%\usepackage{beramono} +%\usepackage{listings} +% The listing settings are now supported by LyX in a separate section "Listings". +\usepackage{xcolor} + +\definecolor{scalakeyword}{rgb}{0.16,0.07,0.5} +\definecolor{dkgreen}{rgb}{0,0.6,0} +\definecolor{gray}{rgb}{0.5,0.5,0.5} +\definecolor{mauve}{rgb}{0.58,0,0.82} +\definecolor{aqua}{rgb}{0.9,0.96,0.999} +\definecolor{scalatype}{rgb}{0.2,0.3,0.2} +\definecolor{teal}{rgb}{0,0.6,0} + +% These settings are now in the Listings tab in LyX. +%\lstdefinestyle{myScalastyle}{ +% language=scala, % This should be defined first!!! Otherwise it overrides all customization via morekeywords / otherkeywords. +% otherkeywords={{=,=>,<-,<\%,<:,>:,\#,@,*,+,-,/,::,:,[,]}}, +% frame=tb, +% aboveskip=2mm, +% belowskip=2mm, +% showstringspaces=false, +% columns=flexible, +% basicstyle={\small\ttfamily}, +% extendedchars=true, +% %numbers=none, +% numberstyle=\tiny\color{gray}, +% keywordstyle=\color{blue}, +% commentstyle=\color{dkgreen}, +% stringstyle=\color{mauve}, +% frame=single, +% framerule=0.01mm, +% breaklines=true, +% breakatwhitespace=true, +% tabsize=3, +% framexleftmargin=4mm, framexrightmargin=4mm, +% xleftmargin=4mm, xrightmargin=4mm, % Making these margins the same has a good effect. +% framextopmargin=0.5mm, framexbottommargin=.5mm, +% fillcolor=\color{aqua}, +% rulecolor=\color{aqua}, +% rulesepcolor=\color{aqua}, +% backgroundcolor=\color{aqua}, +% mathescape=true, +%} + +% Example usage: \begin{lstlisting}[style=myScalastyle] object blah \end{lstlisting} +%\newenvironment{scala}{\begin{lstlisting}[style=myScalastyle]}{\end{lstlisting}} +%\lstnewenvironment{scala}{\lstset{style=myScalastyle}}{} + +\usepackage[nocenter]{qtree} % simple tree drawing +\usepackage{relsize} % make math symbols larger or smaller; supports \smaller etc. +\usepackage{stmaryrd} % some extra symbols such as \fatsemi +% Note: using \forwardcompose inside a \text{} will cause a LaTeX error! +\newcommand{\forwardcompose}{\hspace{1.2pt}\ensuremath\mathsmaller{\fatsemi}\hspace{1.5pt}} +% this is ugly, I used this before I found \fatsemi: +%\newcommand{\bef}{\hspace{1.0pt}\ensuremath\raisebox{2pt}{$\mathsmaller{\mathsmaller{\circ}}$}\hspace{-2.9pt},} +%\makeatletter +% Macros to assist LyX with XYpic when using scaling. +\newcommand{\xyScaleX}[1]{% +\makeatletter +\xydef@\xymatrixcolsep@{#1} +\makeatother +} % end of \xyScaleX +\makeatletter +\newcommand{\xyScaleY}[1]{% +\makeatletter +\xydef@\xymatrixrowsep@{#1} +\makeatother +} % end of \xyScaleY + +% Increase the default vertical space inside table cells. +\renewcommand\arraystretch{1.4} + +% Color for PDF hyperlinks. +\definecolor{hlink}{rgb}{0.06, 0.14, 0.48} + +% Make underline green. +\definecolor{greenunder}{rgb}{0.1,0.6,0.2} +%\newcommand{\munderline}[1]{{\color{greenunder}\underline{{\color{black}#1}}\color{black}}} +\def\mathunderline#1#2{\color{#1}\underline{{\color{black}#2}}\color{black}} +% The LyX document will define a macro \gunderline{#1} that will use \mathunderline with the color `greenunder`. +%\def\gunderline#1{\mathunderline{greenunder}{#1}} % This is now defined by LyX itself with GUI support. + + +% Prepare settings for imposing a color background for all displayed math. This will be done by a script later. +\usepackage{empheq} % Background on all displayed equations. +\definecolor{mathbg}{rgb}{1.0, .98, .87} +\newcommand*\mymathbgbox[1]{% +\setlength{\fboxsep}{0pt}% +\colorbox{mathbg}{\hspace{0.5mm}#1\hspace{0.5mm}}} +%\renewenvironment{align}{% +%\begin{empheq}[box=\mymathbgbox]{align}}{% +%\endalign\end{empheq}} +% Run a command such as LC_ALL=C sed -i bak -e 's|\\begin{align}|\\begin{empheq}[box=\\mymathbgbox]{align}|; s|\\end{align}|\\end{empheq}|' sofp-filterable.tex +% This is not used now because the results are not great. + +% Better text quotes. +\renewcommand\textquotedblleft{``} +\renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} +\end_preamble +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt +\use_default_options true +\master sofp.lyx +\maintain_unincluded_children false +\language english +\language_package default +\inputencoding auto +\fontencoding global +\font_roman "palatino" "default" +\font_sans "helvet" "default" +\font_typewriter "cmtt" "default" +\font_math "newtxmath" "auto" +\font_default_family default +\use_non_tex_fonts false +\font_sc false +\font_osf false +\font_sf_scale 100 100 +\font_tt_scale 100 100 +\use_microtype false +\use_dash_ligatures false +\graphics default +\default_output_format default +\output_sync 0 +\bibtex_command default +\index_command makeindex +\paperfontsize 10 +\spacing single +\use_hyperref true +\pdf_title "The Science of Functional Programming: A Tutorial, with Examples in Scala" +\pdf_author "Sergei Winitzki" +\pdf_subject "Functional programming" +\pdf_keywords "Functional programming, Scala, Type theory, Category theory, Formal logic, Programming languages" +\pdf_bookmarks true +\pdf_bookmarksnumbered true +\pdf_bookmarksopen true +\pdf_bookmarksopenlevel 2 +\pdf_breaklinks true +\pdf_pdfborder true +\pdf_colorlinks true +\pdf_backref page +\pdf_pdfusetitle true +\pdf_quoted_options "linkcolor=hlink" +\papersize custom +\use_geometry true +\use_package amsmath 1 +\use_package amssymb 1 +\use_package cancel 1 +\use_package esint 1 +\use_package mathdots 1 +\use_package mathtools 1 +\use_package mhchem 1 +\use_package stackrel 1 +\use_package stmaryrd 1 +\use_package undertilde 1 +\cite_engine basic +\cite_engine_type default +\biblio_style plain +\use_bibtopic false +\use_indices false +\paperorientation portrait +\suppress_date false +\justification false +\use_refstyle 1 +\use_minted 0 +\index Index +\shortcut idx +\color #008000 +\end_index +\paperwidth 7.444in +\paperheight 9.68in +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm +\secnumdepth 3 +\tocdepth 2 +\paragraph_separation indent +\paragraph_indentation default +\is_math_indent 0 +\math_numbering_side default +\quotes_style english +\dynamic_quotes 0 +\papercolumns 1 +\papersides 2 +\paperpagestyle default +\listings_params "language=Scala,morekeywords={{scala}},otherkeywords={=,=>,<-,<\%,<:,>:,\#,@,:,[,],.,???},keywordstyle={\color{scalakeyword}},morekeywords={[2]{String,Short,Int,Long,Char,Boolean,Double,Float,BigDecimal,Seq,Map,Set,Option,Either,Future,Successful,LazyList,Vector,Range,IndexedSeq,true,false,None,List,Nil,Try,Success,Failure,Some,Left,Right,Nothing,Any,Array,Unit,Iterator,Stream,Throwable,Integer,Object}},keywordstyle={[2]{\color{scalatype}}},frame=tb,aboveskip={1.5mm},belowskip={0.5mm},showstringspaces=false,columns=fullflexible,keepspaces=true,basicstyle={\smaller\ttfamily},extendedchars=true,numbers=none,numberstyle={\tiny\color{gray}},commentstyle={\color{dkgreen}},stringstyle={\color{mauve}},frame=single,framerule={0.0mm},breaklines=true,breakatwhitespace=true,tabsize=3,framexleftmargin={0.5mm},framexrightmargin={0.5mm},xleftmargin={1.5mm},xrightmargin={1.5mm},framextopmargin={0.5mm},framexbottommargin={0.5mm},fillcolor={\color{aqua}},rulecolor={\color{aqua}},rulesepcolor={\color{aqua}},backgroundcolor={\color{aqua}},mathescape=false,extendedchars=true" +\tracking_changes false +\output_changes false +\html_math_output 0 +\html_css_as_file 0 +\html_be_strict false +\end_header + +\begin_body + +\begin_layout Addchap +Essay: Software engineers and software artisans +\end_layout + +\begin_layout Standard +Let us examine what we ordinarily understand by +\emph on +engineering +\emph default + as opposed to artisanship or craftsmanship. + It will then become apparent that today's computer programmers must be + viewed as +\begin_inset Quotes eld +\end_inset + +software artisans +\begin_inset Quotes erd +\end_inset + + rather than software engineers. +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout +The book reviewed in +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "https://www.developerdotstar.com/mag/bookreviews/bitner_craftsmanship.html" +literal "false" + +\end_inset + + +\family default + proposes a different definition of +\begin_inset Quotes eld +\end_inset + +software artisans +\begin_inset Quotes erd +\end_inset + + than this book, but agrees that software developers work largely as artisans. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Addsec +Engineering disciplines +\end_layout + +\begin_layout Standard +Consider the way mechanical engineers, chemical engineers, or electrical + engineers work, and look at the studies they require for becoming proficient + in their fields. +\end_layout + +\begin_layout Standard +A mechanical engineer studies calculus, linear algebra, differential geometry, + and several areas of physics such as theoretical mechanics, thermodynamics, + and elasticity theory, +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.colorado.edu/mechanical/academics/undergraduate-program/curriculum" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + and then uses calculations to guide the design of a bridge. + A chemical engineer studies chemistry, thermodynamics, calculus, linear + algebra, differential equations, some areas of physics such as thermodynamics + and kinetic theory, +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.colorado.edu/engineering/sample-undergraduate-curriculum-chemical" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + and uses calculations to guide the design of a chemical process. + An electrical engineer studies advanced calculus, linear algebra, and several + areas of physics such as electrodynamics and quantum theory, +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://archive.is/XYLyE" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + and uses calculations to design an antenna or a microchip. +\end_layout + +\begin_layout Standard +The common pattern is that engineers use mathematics and natural sciences + in order to create new devices. + Mathematical calculations and scientific reasoning are performed +\emph on +before +\emph default + designing or building a real machine. +\end_layout + +\begin_layout Standard +Some of the studies required for engineers include arcane abstract concepts + such as the +\begin_inset Quotes eld +\end_inset + +Fourier transform of the delta function +\begin_inset Quotes erd +\end_inset + + +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.youtube.com/watch?v=KAbqISZ6SHQ" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + and the +\begin_inset Quotes eld +\end_inset + +inverse +\begin_inset Formula $Z$ +\end_inset + +-transform +\begin_inset Quotes erd +\end_inset + + +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://archive.is/SsJqP" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + in digital signal processing, +\begin_inset Quotes eld +\end_inset + +rank 4 tensors +\begin_inset Quotes erd +\end_inset + + +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://serc.carleton.edu/NAGTWorkshops/mineralogy/mineral_physics/tensors.html" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + in calculations of elasticity of materials, +\begin_inset Quotes eld +\end_inset + +Lagrangians with non-holonomic constraints +\begin_inset Quotes erd +\end_inset + + +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://arxiv.org/abs/math/0008147" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + in robotics, and the +\begin_inset Quotes eld +\end_inset + +Gibbs free energy +\begin_inset Quotes erd +\end_inset + + in chemical reactor design. +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.amazon.com/Introduction-Chemical-Engineering-Kinetics-Reactor/dp/1118368258" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +To be sure, a significant part of what engineers do is not covered by any + theory: the +\emph on +know-how +\emph default +, the informal reasoning, the traditional knowledge passed on from expert + to novice, — all those skills that are hard to formalize are important. + Nevertheless, engineering is crucially based on natural science and mathematics + for some of its decision-making about new designs. +\end_layout + +\begin_layout Addsec +Artisanship: Trades and crafts +\end_layout + +\begin_layout Standard +Now consider what kinds of things shoemakers, plumbers, or home painters + do, and what they have to learn in order to become proficient in their + profession. +\end_layout + +\begin_layout Standard +A novice shoemaker, for example, begins by copying some drawings +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://youtu.be/cY5MY0czMAk?t=141" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + and goes on to cutting leather in a home workshop. + Apprenticeships proceed via learning by doing, with comments and instruction + from an expert. + After a few years of study (for example, a painter apprenticeship in California + can be as short as two years +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://www.calapprenticeship.org/programs/painter_apprenticeship.php" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + +), a new artisan is ready to start productive work. + +\end_layout + +\begin_layout Standard +All trades operate entirely from tradition and practical experience. + It takes a prolonged learning effort to become a good artisan in any profession. + But the trades do not require academic study because there is no formal + theory from which to proceed. + There are no Fourier transforms applied to delta functions, no Lagrangians + with non-holonomic constraints, no fourth rank tensors to calculate, nor + any differential equations to solve. +\end_layout + +\begin_layout Standard +Artisans do not study science or mathematics because their professions do + not make use of any formal theory for guiding their designs or processes. +\end_layout + +\begin_layout Addsec +Programmers today are artisans, not engineers +\end_layout + +\begin_layout Standard +Programmers are +\emph on +not engineers +\emph default + in the sense normally used in the engineering professions. +\end_layout + +\begin_layout Subsection* +No requirements of licensing or formal study +\end_layout + +\begin_layout Standard +Mechanical, electrical, chemical engineers are required to pass a license + exam to become certified to work. + The license exam certifies that the person is proficient in applying a + known set of engineering tools and methods. + But in software engineering, no certifications or licenses are required + for the job (although many certification programs exist). +\end_layout + +\begin_layout Standard +Working software engineers are also not required to have studied software + engineering or computer science (CS). + According to a recent Stack Overflow survey, +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://thenextweb.com/insider/2016/04/23/dont-need-go-college-anymore-programmer/" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + about 56% of working programmers have no CS degree. + The author of this book is a self-taught programmer who has degrees in + physics but never formally studied CS or taken any academic courses in + algorithms, data structures, computer networks, compilers, programming + languages, or other standard CS topics. + +\end_layout + +\begin_layout Standard +A large fraction of successful programmers have no college degrees and perhaps + +\emph on +never +\emph default + studied formally. + They acquired all their knowledge and skills through self-study and practical + work. + A prominent example is Robert C. +\begin_inset space ~ +\end_inset + +Martin +\begin_inset Index idx +status open + +\begin_layout Plain Layout +Robert C. +\begin_inset space ~ +\end_inset + +Martin +\end_layout + +\end_inset + +, +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://en.wikipedia.org/wiki/Robert_C._Martin" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + an outspoken guru in the arts of programming. + He routinely refers to programmers as artisans +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://blog.cleancoder.com/uncle-bob/2013/02/01/The-Humble-Craftsman.html" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + and uses the appropriate imagery: novices and masters, trade and craft, + the honor of the guild, etc. + He compares programmers to plumbers, electricians, lawyers, and surgeons, + but never to mathematicians, physicists, or engineers of any kind. + According to one of his blog posts, +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://blog.cleancoder.com/uncle-bob/2013/11/25/Novices-Coda.html" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + he started working at age 17 as a self-taught programmer. + He never went to college and holds no degrees. +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://hashnode.com/post/i-am-robert-c-martin-uncle-bob-ask-me-anything-cjr7pnh8g000k2cs18o5nhulp/2" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + It is clear that R. +\begin_inset space ~ +\end_inset + +C. +\begin_inset space ~ +\end_inset + +Martin +\emph on +is +\emph default + an expert craftsman and that he did +\emph on +not +\emph default + need academic study to master the craft of programming. +\end_layout + +\begin_layout Standard +Here is another opinion +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://archive.is/tAKQ3" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + (emphasis is theirs): +\end_layout + +\begin_layout Quotation + +\size small +Software Engineering is unique among the STEM careers in that it absolutely + does +\emph on +not +\emph default + require a college degree to be successful. + It most certainly does not require licensing or certification. + +\emph on +It requires experience +\emph default +. +\end_layout + +\begin_layout Standard +This description fits a career in crafts — but certainly not a career, say, + in electrical engineering. +\end_layout + +\begin_layout Standard +The high demand for software developers gave rise to +\begin_inset Quotes eld +\end_inset + +developer boot camps +\begin_inset Quotes erd +\end_inset + + +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://archive.is/GkOL9" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + — vocational schools that educate new programmers in a few months through + purely practical training, with no formal theory or mathematics involved. + These vocational schools are successful +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://archive.is/E9FXP" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + in job placement. + But it is unimaginable that a +\begin_inset Formula $6$ +\end_inset + +-month crash course or even a +\begin_inset Formula $2$ +\end_inset + +-year vocational school could prepare engineers to work successfully on + designing, say, analog quantum computers +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.dwavesys.com/quantum-computing" + +\end_inset + + +\end_layout + +\end_inset + + without ever learning quantum physics or calculus. +\end_layout + +\begin_layout Subsection* +No mathematical formalism guides software development +\end_layout + +\begin_layout Standard +Most books on software engineering contain no formulas or equations, no + mathematical derivations, and no precise definitions of the various technical + terms they are using (such as +\begin_inset Quotes eld +\end_inset + +object-oriented +\begin_inset Quotes erd +\end_inset + + or +\begin_inset Quotes eld +\end_inset + +module's responsibilities +\begin_inset Quotes erd +\end_inset + +). + Some of those books +\begin_inset Foot +status open + +\begin_layout Plain Layout +E.g., +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "https://www.amazon.com/Object-Oriented-Software-Engineering-Unified-Methodology/dp/0073376256" + +\end_inset + + +\end_layout + +\end_inset + + also have almost no program code in them. + Some of those books are written by practitioners such as R. +\begin_inset space \space{} +\end_inset + +C. +\begin_inset space \space{} +\end_inset + +Martin never studied any formalisms and do not think in terms of formalisms. + Instead, they summarize their programming experience in vaguely formulated + heuristic “principles”. +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://blog.cleancoder.com/uncle-bob/2016/03/19/GivingUpOnTDD.html" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + The programmers are told: +\begin_inset Quotes eld +\end_inset + +code is about detail +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +never abandon the big picture +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +avoid tight coupling in your modules +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +a class must serve a single responsibility +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +strive for good interfaces +\begin_inset Quotes erd +\end_inset + +, etc. + +\end_layout + +\begin_layout Standard +In contrast, textbooks on mechanical or electrical engineering include a + significant amount of mathematics. + The design of a microwave antenna is guided not by an +\begin_inset Quotes eld +\end_inset + +open and closed module principle +\begin_inset Quotes erd +\end_inset + + but by solving the relevant differential equations +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://youtu.be/8KpfVsJ5Jw4?t=447" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + of electrodynamics. +\end_layout + +\begin_layout Standard +Another example of programmers' avoidance of mathematical tools is given + by the +\begin_inset Quotes eld +\end_inset + +Liskov substitution principle +\begin_inset Index idx +status open + +\begin_layout Plain Layout +Liskov substitution principle +\end_layout + +\end_inset + + +\begin_inset Quotes erd +\end_inset + + for subtyping +\begin_inset Index idx +status open + +\begin_layout Plain Layout +subtyping +\end_layout + +\end_inset + +. +\begin_inset Foot +status open + +\begin_layout Plain Layout +See +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "https://en.wikipedia.org/wiki/Liskov_substitution_principle" +literal "false" + +\end_inset + + +\family default +. + The LSP always holds in functional programming if values are immutable + and subtyping is viewed as an automatic type conversion function (see Definitio +n +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Definition-subtyping" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). + A property +\begin_inset Formula $\phi(y)$ +\end_inset + + is rewritten as +\begin_inset Formula $\phi(c(y))$ +\end_inset + + by inserting a suitable type conversion function, +\begin_inset Formula $c:S\rightarrow T$ +\end_inset + +. + Since +\begin_inset Formula $c(y)$ +\end_inset + + has type +\begin_inset Formula $T$ +\end_inset + + and +\begin_inset Formula $\phi(x)$ +\end_inset + + holds for all values +\begin_inset Formula $x:T$ +\end_inset + +, we find that the property +\begin_inset Formula $\phi(c(y))$ +\end_inset + + holds automatically. +\end_layout + +\end_inset + + Its rigorous formulation ( +\begin_inset Quotes eld +\end_inset + +for any property +\begin_inset Formula $\phi(x)$ +\end_inset + + that holds for all +\begin_inset Formula $x$ +\end_inset + + of type +\begin_inset Formula $T$ +\end_inset + +, and for any subtype +\begin_inset Formula $S$ +\end_inset + + of +\begin_inset Formula $T$ +\end_inset + +, the property +\begin_inset Formula $\phi(y)$ +\end_inset + + must also hold for all +\begin_inset Formula $y$ +\end_inset + + of type +\begin_inset Formula $S$ +\end_inset + + +\begin_inset Quotes erd +\end_inset + +) is not used by programmers. + Instead, the literature on object-oriented programming formulates the principle + as +\begin_inset Quotes eld +\end_inset + +objects of type +\begin_inset Formula $T$ +\end_inset + + may be substituted by objects of type +\begin_inset Formula $S$ +\end_inset + + while keeping the correctness of the program +\begin_inset Quotes erd +\end_inset + +. + This formulation +\begin_inset Index idx +status open + +\begin_layout Plain Layout +object-oriented programming +\end_layout + +\end_inset + + is both vague (it does not specify how to choose the substituted objects + of type +\begin_inset Formula $S$ +\end_inset + +) and, strictly speaking, incorrect: If the program contains a function + +\begin_inset Formula $f(t)$ +\end_inset + + where +\begin_inset Formula $t$ +\end_inset + + is a value of type +\begin_inset Formula $T$ +\end_inset + +, it is not always possible to find some value +\begin_inset Formula $s$ +\end_inset + + of type +\begin_inset Formula $S$ +\end_inset + + such that +\begin_inset Formula $f(s)=f(t)$ +\end_inset + +. + The reason is that some subtyping relations are not surjective, as shown + in Section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Subtyping-with-injective" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + of this book. +\end_layout + +\begin_layout Standard +Donald Knuth's classic textbook +\begin_inset Quotes eld +\end_inset + + +\emph on +The Art of Programming +\emph default + +\begin_inset Quotes erd +\end_inset + + indeed treats programming as an art and not as a science. + Knuth shows many algorithms and derives their mathematical properties but + gives almost no examples of realistic program code and does not provide + any theory that could guide programmers in actually +\emph on +writing +\emph default + programs (say, choosing the data types to be used). + Knuth assumes that the reader who understands the mathematical properties + of an algorithm will be able +\emph on +somehow +\emph default + to write correct code. +\end_layout + +\begin_layout Standard +The books +\begin_inset Quotes eld +\end_inset + +The Science of Programming +\begin_inset Quotes erd +\end_inset + + +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.amazon.com/Science-Programming-Monographs-Computer/dp/0387964800" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + and +\begin_inset Quotes eld +\end_inset + +Program derivation +\begin_inset Quotes erd +\end_inset + + +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.amazon.com/Program-Derivation-Development-Specifications-International/dp/0201416247" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + are attempts to provide a mathematical basis for writing programs starting + from formal specifications. + The books give some methods that guide programmers in writing code and + at the same time produce a proof that the code conforms to the specification. + However, the scope of proposed methods is limited to designing algorithms + for iterative manipulation of data, such as sorting and searching algorithms. + The procedures suggested in those books are far from a formal mathematical + +\emph on +derivation +\emph default + of a wide range of software programs from specifications. + In any case, most programmers today are unaware of these books and do not + use the methods explained there, even when those methods could apply. +\end_layout + +\begin_layout Standard +Today's computer science courses do not teach a true engineering approach + to software construction. + Some courses teach analysis of programs using mathematical methods. + Two such methods are complexity analysis +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.cs.cmu.edu/~adamchik/15-121/lectures/Algorithmic%20Complexity/complexity.html" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + (the +\begin_inset Quotes eld +\end_inset + +big- +\begin_inset Formula $O$ +\end_inset + + notation +\begin_inset Quotes erd +\end_inset + +) and formal verification. +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://en.wikipedia.org/wiki/Formal_verification" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + But programs are analyzed or verified only +\emph on +after +\emph default + they are somehow written. + Theory does not guide the actual +\emph on +process +\emph default + of writing code: it does not define good ways of organizing the code (e.g., + how to decompose the code into modules, classes, or functions) and does + not tell programmers which data structures and type signatures of functions + will be useful to implement. + Programmers make these design decisions on the basis of experience and + intuition, trial-and-error, copy-paste, guesswork, and debugging. + +\end_layout + +\begin_layout Standard +In a sense, program analysis and verification is analogous to writing mathematic +al equations for the surface of a shoe made by a fashion designer. + The resulting +\begin_inset Quotes eld +\end_inset + +shoe equations +\begin_inset Quotes erd +\end_inset + + are mathematically rigorous and can be analyzed or +\begin_inset Quotes eld +\end_inset + +verified +\begin_inset Quotes erd +\end_inset + +. + But the equations are merely written after the fact, they do not guide + the fashion designers in actually making shoes. + It is understandable that fashion designers do not study the mathematical + theory of surfaces. +\end_layout + +\begin_layout Subsection* +Programmers avoid academic terminology +\end_layout + +\begin_layout Standard +Programmers jokingly grumble about terms such as +\begin_inset Quotes eld +\end_inset + +functor +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +monad +\begin_inset Quotes erd +\end_inset + +, or +\begin_inset Quotes eld +\end_inset + +lambda-functions +\begin_inset Quotes erd +\end_inset + +: +\end_layout + +\begin_layout Quote + +\size small +Those fancy words used by functional programmers purists really annoy me. + Monads, functors... + Nonsense!!! +\size default + +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://archive.is/65K3D" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Perhaps only a small minority of software developers complain about this, + as the majority seems to be unaware of +\begin_inset Quotes eld +\end_inset + +applicative functors +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +free monads +\begin_inset Quotes erd +\end_inset + +, and other arcane terminology. + Indeed, that sort of terminology is intentionally avoided by most books + and tutorials aimed at programmers. +\end_layout + +\begin_layout Standard +But why would a software +\emph on +engineer +\emph default + wince at +\begin_inset Quotes eld +\end_inset + +functors +\begin_inset Quotes erd +\end_inset + + or at having to verify the laws of a +\begin_inset Quotes eld +\end_inset + +monad +\begin_inset Quotes erd +\end_inset + +? Other branches of engineering use lots of terminology that is far from + self-explanatory and requires some study. + Chemical engineers learn about +\begin_inset Quotes eld +\end_inset + +Gibbs free energy +\begin_inset Quotes erd +\end_inset + +, which is a technical term that denotes a certain function. + (It +\begin_inset Index idx +status open + +\begin_layout Plain Layout +jokes +\end_layout + +\end_inset + + does not mean getting energy from J. +\begin_inset space ~ +\end_inset + +W. +\begin_inset space ~ +\end_inset + +Gibbs +\begin_inset Index idx +status open + +\begin_layout Plain Layout +Josiah Willard Gibbs +\end_layout + +\end_inset + + for free!) Chemical engineers accept the need for studying +\begin_inset Quotes eld +\end_inset + +phase diagrams +\begin_inset Quotes erd +\end_inset + + or +\begin_inset Quotes eld +\end_inset + +Fourier's law +\begin_inset Quotes erd +\end_inset + +. + Electrical engineers do not avoid +\begin_inset Quotes eld +\end_inset + +Fourier transforms +\begin_inset Quotes erd +\end_inset + + or +\begin_inset Quotes eld +\end_inset + +delta functions +\begin_inset Quotes erd +\end_inset + + because those are weird things to say. + Mechanical engineers take it for granted that they need +\begin_inset Quotes eld +\end_inset + +rank 4 tensors +\begin_inset Quotes erd +\end_inset + +, +\begin_inset Quotes eld +\end_inset + +Lagrangians +\begin_inset Quotes erd +\end_inset + +, and +\begin_inset Quotes eld +\end_inset + +non-holonomic constraints +\begin_inset Quotes erd +\end_inset + +. + The arcane terminology seems to be the least of their difficulties, as + their textbooks are full of complicated equations and long derivations. +\end_layout + +\begin_layout Standard +Textbooks on true software engineering would have been full of equations + and derivations, teaching engineers how to perform certain calculations + that are required +\emph on +before +\emph default + starting to write code. +\end_layout + +\begin_layout Addsec +Towards true engineering in software +\end_layout + +\begin_layout Standard +It is now clear that we do not presently have true software engineering. + The people employed under that job title are actually artisans. + They work using artisanal methods, and their culture and processes are + that of a crafts guild. +\end_layout + +\begin_layout Standard +True software engineering means having a mathematical theory that guides + the process of writing programs, — not just theory that describes or analyzes + programs after they are +\emph on +somehow +\emph default + written. +\end_layout + +\begin_layout Standard +It is not enough that the numerical methods required for physics or the + matrix calculations required for data science are +\begin_inset Quotes eld +\end_inset + +mathematical +\begin_inset Quotes erd +\end_inset + +. + These programming tasks are indeed formulated using mathematical theory. + However, mathematical +\emph on +subject matter +\emph default + (aerospace control, physics or astronomy simulations, or statistics) does + not mean that mathematics is used to guide the process of writing code. + Data scientists, aerospace engineers, and physicists almost always work + as artisans when converting their computations into program code. +\end_layout + +\begin_layout Standard +We expect that software engineers +\family sans +' +\family default + textbooks should be full of equations and derivations. + What theory would those equations represent? +\end_layout + +\begin_layout Standard +This theory is what this book calls +\series bold +applied functional type theory +\series default + +\begin_inset Index idx +status open + +\begin_layout Plain Layout +applied functional type theory +\end_layout + +\end_inset + + (see Chapter +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "chap:Applied-functional-type" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). + It represents the mathematical foundation of the modern practice of functional + programming, as implemented in languages such as OCaml, Haskell, and Scala. + This theory is a blend of set theory, category theory, and logical proof + theory, adapted for the needs of programmers. + It has been in development since late 1990s and is still being actively + worked on by a community of software practitioners and academic computer + scientists. +\end_layout + +\begin_layout Standard +To appreciate that functional programming, unlike any other programming + paradigm, is based on a +\emph on +theory that guides coding +\emph default +, we can look at some recent software engineering conferences such as +\begin_inset Quotes eld +\end_inset + +Scala By the Bay +\begin_inset Quotes erd +\end_inset + + +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://2015.scala.bythebay.io/" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + or BayHac, +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://bayhac.org/" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + or at the numerous FP-related online tutorials and blogs. + We cannot fail to notice that speakers devote significant time to a peculiar + kind of applied mathematical reasoning. + Rather than focusing on one or another API or algorithm, as it is often + the case with other software engineering blogs or presentations, an FP + speaker describes a +\emph on +mathematical structure +\emph default + — such as the +\begin_inset Quotes eld +\end_inset + +applicative functor +\begin_inset Quotes erd +\end_inset + + +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://www.youtube.com/watch?v=bmIxIslimVY" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + or the +\begin_inset Quotes eld +\end_inset + +free monad +\begin_inset Quotes erd +\end_inset + + +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://www.youtube.com/watch?v=U0lK0hnbc4U" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + — and illustrates its use for practical coding. +\end_layout + +\begin_layout Standard +These people are not graduate students showing off their theoretical research. + They are practitioners, software engineers who use FP on their jobs. + It is just the nature of FP that certain mathematical tools and constructions + are directly applicable to practical programming tasks. +\end_layout + +\begin_layout Standard +These mathematical tools are not mere tricks for a specific programming + language; they apply equally to all FP languages. + Before starting to write code, the programmer can jot down certain calculations + in a mathematical notation (see Fig. +\begin_inset space \space{} +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "fig:Example-calculation-in-type-theory" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). + The results of those calculations will help design the code fragment the + programmer is about to write. + This activity is similar to that of an engineer who performs some mathematical + calculations before embarking on a design project. + +\begin_inset Wrap figure +lines 0 +placement L +overhang 0in +width "50text%" +status open + +\begin_layout Plain Layout +\align center + +\size footnotesize +\begin_inset VSpace 25baselineskip% +\end_inset + + +\begin_inset Graphics + filename ftt-example.jpg + width 100line% + +\end_inset + + +\begin_inset VSpace -25baselineskip% +\end_inset + + +\end_layout + +\begin_layout Plain Layout + +\size footnotesize +\begin_inset Caption Standard + +\begin_layout Plain Layout +A programmer performs a derivation before writing Haskell code. +\begin_inset CommandInset label +LatexCommand label +name "fig:Example-calculation-in-type-theory" + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Plain Layout +\begin_inset VSpace -50baselineskip% +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\noindent +A recent example of a development in applied functional type theory is the + +\begin_inset Quotes eld +\end_inset + +free applicative functor +\begin_inset Quotes erd +\end_inset + + construction. + It was first described in a 2014 paper; +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://arxiv.org/pdf/1403.0749.pdf" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + a couple of years later, a combined free applicative / free monad data + type was designed and its implementation proposed in Scala +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://github.com/typelevel/cats/issues/983" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + as well as in Haskell. +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://elvishjerricco.github.io/2016/04/08/applicative-effects-in-free-monads.html" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + This technique allows programmers to implement declarative side-effect + computations where some parts are sequential but other parts are computed + in parallel, and to achieve the parallelism +\emph on +automatically +\emph default + while maintaining the composability of the resulting programs. + The new technique has advantages over monad transformers, which was a previousl +y known method of composing declarative side-effects. + The combined +\begin_inset Quotes eld +\end_inset + +free applicative / free monad +\begin_inset Quotes erd +\end_inset + + code was designed and implemented by true software engineers. + They first derived the type constructor that has the necessary algebraic + properties. + Guided by the resulting type formula, they wrote code that was guaranteed + to work as intended. +\end_layout + +\begin_layout Standard +Another example of using applied functional type theory is the +\begin_inset Quotes eld +\end_inset + + +\begin_inset Index idx +status open + +\begin_layout Plain Layout +tagless final +\end_layout + +\end_inset + +tagless final +\begin_inset Quotes erd +\end_inset + + encoding of effects, first described +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://okmij.org/ftp/tagless-final/index.html" + +\end_inset + + +\end_layout + +\end_inset + + in 2009. + That technique (called +\begin_inset Quotes eld +\end_inset + +Church-encoded free monad +\begin_inset Index idx +status open + +\begin_layout Plain Layout +free monad +\end_layout + +\end_inset + + +\begin_inset Quotes erd +\end_inset + + in the present book) has advantages over the ordinary free monad in a number + of cases — just as the free monad itself was used to cure certain problems + with monad transformers. +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://blog.ezyang.com/2013/09/if-youre-using-lift-youre-doing-it-wrong-probably/" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + The new encoding is not tied to a specific programming language. + Rather, it is a language-agnostic construction that was originally described + in OCaml and later used in Haskell and Scala, but can be made to work even + in Java, +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://oleksandrmanzyuk.wordpress.com/2014/06/18/" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + which is not an FP language. +\end_layout + +\begin_layout Standard +This example shows that we may need several more years of work before the + practical aspects of using applied functional type theory are sufficiently + well understood by the FP community. + The theory is in active development, and its design patterns — as well + as the exact scope of the requisite theoretical material — are still being + figured out. + If the 40-year gap hypothesis +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.linkedin.com/pulse/40-year-gap-what-has-academic-computer-science-ever-done-winitzki" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + holds, we should expect applied functional type theory (perhaps under a + different name) to become mainstream by 2030. + This book is a step towards a clear designation of the scope of that theory. +\end_layout + +\begin_layout Addsec +Does software need engineers, or are artisans sufficient? +\end_layout + +\begin_layout Standard +The demand for programmers is growing. + +\begin_inset Quotes eld +\end_inset + +Software developer +\begin_inset Quotes erd +\end_inset + + was #1 best job +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://money.usnews.com/money/careers/articles/how-us-news-ranks-the-best-jobs" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + in the US in 2018. + But is there a demand for engineers or just for artisans? +\end_layout + +\begin_layout Standard +We do not seem to be able to train enough software artisans. +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://archive.is/137b8" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + So, it is probably impossible to train as many software engineers in the + true sense of the word. + Modern computer science courses do not actually train engineers in that + sense. + Instead, they train academic researchers who can also work as software + artisans and write code. +\end_layout + +\begin_layout Standard +Looking at the situation in construction business in the U.S.A., we find that + it employs about +\begin_inset Formula $10$ +\end_inset + + times more construction workers as architects. + We might conclude that perhaps one software engineer is required per dozen + software artisans. +\end_layout + +\begin_layout Standard +What is the price of +\emph on +not +\emph default + having engineers, of replacing them with artisans? +\end_layout + +\begin_layout Standard +Software practitioners have long bemoaned the permanent state of +\begin_inset Quotes eld +\end_inset + +crisis +\begin_inset Quotes erd +\end_inset + + in software development. + Code +\begin_inset Quotes eld +\end_inset + +rots with time +\begin_inset Quotes erd +\end_inset + +, its complexity grows +\begin_inset Quotes eld +\end_inset + +out of control +\begin_inset Quotes erd +\end_inset + +, and operating systems have been notorious for ever-appearing security + flaws +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "http://archive.fo/HtQzw" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + despite many thousands of programmers and testers employed. + It appears that the growing complexity of software tends to overwhelm the + capacity of the human brain for correct +\emph on +artisanal +\emph default + programming. +\end_layout + +\begin_layout Standard +It is precisely in designing large and robust software systems that we would + benefit from true engineering. + Artisans has been building bridges and using chemical reactions by trial + and error and via tradition, long before mechanical or chemical engineering + disciplines were developed and founded upon rigorous theory. + But once the theory became available, engineers were able to design unimaginabl +y more powerful and complicated structures, devices, and processes. + So, we may expect that trial, error, and adherence to tradition is inadequate + for some of the more complex software development tasks in front of us. + +\end_layout + +\begin_layout Standard +To build large and reliable software, such as new mobile or embedded operating + systems or distributed peer-to-peer trust architectures, we will most likely + need the qualitative increase in productivity and reliability that can + only come from replacing artisanal programming by a true engineering discipline. + Applied functional type theory and functional programming are steps in + that direction. +\end_layout + +\end_body +\end_document diff --git a/sofp-src/sofp-essay2.pre.md b/sofp-src/sofp-essay2.pre.md new file mode 100644 index 000000000..e69de29bb diff --git a/sofp-src/sofp-essays.lyx b/sofp-src/sofp-essay3.lyx similarity index 58% rename from sofp-src/sofp-essays.lyx rename to sofp-src/sofp-essay3.lyx index bfc6a7bad..aa84c1f4c 100644 --- a/sofp-src/sofp-essays.lyx +++ b/sofp-src/sofp-essay3.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -273,7 +288,7 @@ \begin_body -\begin_layout Chapter +\begin_layout Addchap \begin_inset Quotes eld \end_inset @@ -368,7 +383,7 @@ In this book, code examples are written in Scala because the author is fluent the same questions and struggle with the same practical issues. \end_layout -\begin_layout Section +\begin_layout Addsec AFTT is not covered by courses in computer science \end_layout @@ -606,7 +621,7 @@ not \end_layout -\begin_layout Section +\begin_layout Addsec AFTT is not category theory, type theory, or formal logic \end_layout @@ -2448,346 +2463,117 @@ name "fig:Randomly-chosen-pages" \end_layout -\begin_layout Chapter -Essay: Software engineers and software artisans -\end_layout - -\begin_layout Standard -Let us examine what we ordinarily understand by -\emph on -engineering -\emph default - as opposed to artisanship or craftsmanship. - It will then become apparent that today's computer programmers must be - viewed as -\begin_inset Quotes eld -\end_inset - -software artisans -\begin_inset Quotes erd -\end_inset - - rather than software engineers. -\begin_inset Foot -status collapsed - -\begin_layout Plain Layout -The book reviewed in -\family typewriter - -\begin_inset CommandInset href -LatexCommand href -target "https://www.developerdotstar.com/mag/bookreviews/bitner_craftsmanship.html" -literal "false" - -\end_inset - - -\family default - proposes a different definition of -\begin_inset Quotes eld -\end_inset - -software artisans -\begin_inset Quotes erd -\end_inset - - than this book, but agrees that software developers work largely as artisans. -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Section -Engineering disciplines -\end_layout - -\begin_layout Standard -Consider the way mechanical engineers, chemical engineers, or electrical - engineers work, and look at the studies they require for becoming proficient - in their fields. +\begin_layout Addchap +Essay: Why category theory is useful in functional programming \end_layout \begin_layout Standard -A mechanical engineer studies calculus, linear algebra, differential geometry, - and several areas of physics such as theoretical mechanics, thermodynamics, - and elasticity theory, -\begin_inset Foot -status collapsed - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.colorado.edu/mechanical/academics/undergraduate-program/curriculum" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - and then uses calculations to guide the design of a bridge. - A chemical engineer studies chemistry, thermodynamics, calculus, linear - algebra, differential equations, some areas of physics such as thermodynamics - and kinetic theory, -\begin_inset Foot -status collapsed - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.colorado.edu/engineering/sample-undergraduate-curriculum-chemical" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - and uses calculations to guide the design of a chemical process. - An electrical engineer studies advanced calculus, linear algebra, and several - areas of physics such as electrodynamics and quantum theory, -\begin_inset Foot +\begin_inset Index idx status collapsed \begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://archive.is/XYLyE" -literal "false" - -\end_inset - - +category theory!in functional programming \end_layout \end_inset - and uses calculations to design an antenna or a microchip. -\end_layout - -\begin_layout Standard -The common pattern is that engineers use mathematics and natural sciences - in order to create new devices. - Mathematical calculations and scientific reasoning are performed -\emph on -before -\emph default - designing or building a real machine. +This essay is for readers who are already somewhat familiar with category + theory. \end_layout -\begin_layout Standard -Some of the studies required for engineers include arcane abstract concepts - such as the +\begin_layout Addsec +A \begin_inset Quotes eld \end_inset -Fourier transform of the delta function +types/functions \begin_inset Quotes erd \end_inset - -\begin_inset Foot -status collapsed - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.youtube.com/watch?v=KAbqISZ6SHQ" -literal "false" - -\end_inset - - + category for a programming language \end_layout -\end_inset - - and the -\begin_inset Quotes eld -\end_inset - -inverse -\begin_inset Formula $Z$ -\end_inset - --transform -\begin_inset Quotes erd -\end_inset - - -\begin_inset Foot -status collapsed +\begin_layout Standard +We consider programming languages that support various data types, such + as integers ( +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://archive.is/SsJqP" -literal "false" - -\end_inset - - +Int \end_layout \end_inset - in digital signal processing, -\begin_inset Quotes eld -\end_inset - -rank 4 tensors -\begin_inset Quotes erd -\end_inset - - -\begin_inset Foot +), floating-point numbers ( +\begin_inset listings +inline true status open \begin_layout Plain Layout -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://serc.carleton.edu/NAGTWorkshops/mineralogy/mineral_physics/tensors.html" -literal "false" - -\end_inset - - +Float \end_layout \end_inset - in calculations of elasticity of materials, -\begin_inset Quotes eld -\end_inset - -Lagrangians with non-holonomic constraints -\begin_inset Quotes erd -\end_inset - - -\begin_inset Foot -status collapsed +), strings ( +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://arxiv.org/abs/math/0008147" -literal "false" - -\end_inset - - +String \end_layout \end_inset - in robotics, and the -\begin_inset Quotes eld -\end_inset - -Gibbs free energy -\begin_inset Quotes erd -\end_inset - - in chemical reactor design. -\begin_inset Foot -status collapsed +), arrays of strings ( +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.amazon.com/Introduction-Chemical-Engineering-Kinetics-Reactor/dp/1118368258" -literal "false" - -\end_inset - - +Array[String] \end_layout \end_inset - -\end_layout - -\begin_layout Standard -To be sure, a significant part of what engineers do is not covered by any - theory: the -\emph on -know-how -\emph default -, the informal reasoning, the traditional knowledge passed on from expert - to novice, — all those skills that are hard to formalize are important. - Nevertheless, engineering is crucially based on natural science and mathematics - for some of its decision-making about new designs. -\end_layout - -\begin_layout Section -Artisanship: Trades and crafts -\end_layout - -\begin_layout Standard -Now consider what kinds of things shoemakers, plumbers, or home painters - do, and what they have to learn in order to become proficient in their - profession. +), and so on. + Such languages allow programmers to define functions with specified types + of arguments and return values. + The compiler will then verify that all functions are always applied to + arguments of correct types, and also that all return values have the expected + types. + \end_layout \begin_layout Standard -A novice shoemaker, for example, begins by copying some drawings +To each programming language of that kind, there corresponds \begin_inset Foot status open \begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://youtu.be/cY5MY0czMAk?t=141" -literal "false" - +Academically minded computer scientists say that this definition of a +\begin_inset Quotes eld \end_inset - -\end_layout - +types/functions +\begin_inset Quotes erd \end_inset - and goes on to cutting leather in a home workshop. - Apprenticeships proceed via learning by doing, with comments and instruction - from an expert. - After a few years of study (for example, a painter apprenticeship in California - can be as short as 2 years -\begin_inset Foot -status open - -\begin_layout Plain Layout - + category is insufficiently rigorous. + See the discussion in \family typewriter + \begin_inset CommandInset href LatexCommand href -target "http://www.calapprenticeship.org/programs/painter_apprenticeship.php" +name "https://cstheory.stackexchange.com/questions/53389" +target "https://cstheory.stackexchange.com/questions/53389" literal "false" \end_inset @@ -2797,2871 +2583,7 @@ literal "false" \end_inset -), a new artisan is ready to start productive work. - -\end_layout - -\begin_layout Standard -All trades operate entirely from tradition and practical experience. - It takes a prolonged learning effort to become a good artisan in any profession. - But the trades do not require academic study because there is no formal - theory from which to proceed. - There are no Fourier transforms applied to delta functions, no Lagrangians - with non-holonomic constraints, no fourth rank tensors to calculate, nor - any differential equations to solve. -\end_layout - -\begin_layout Standard -Artisans do not study science or mathematics because their professions do - not make use of any formal theory for guiding their designs or processes. -\end_layout - -\begin_layout Section -Programmers today are artisans, not engineers -\end_layout - -\begin_layout Standard -Programmers are -\emph on -not engineers -\emph default - in the sense normally used in the engineering professions. -\end_layout - -\begin_layout Subsection -No requirements of licensing or formal study -\end_layout - -\begin_layout Standard -Mechanical, electrical, chemical engineers are required to pass a license - exam to become certified to work. - The license exam certifies that the person is proficient in applying a - known set of engineering tools and methods. - But in software engineering, no certifications or licenses are required - for the job (although many certification programs exist). -\end_layout - -\begin_layout Standard -Working software engineers are also not required to have studied software - engineering or computer science (CS). - According to a recent Stack Overflow survey, -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://thenextweb.com/insider/2016/04/23/dont-need-go-college-anymore-programmer/" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - about 56% of working programmers have no CS degree. - The author of this book is a self-taught programmer who has degrees in - physics but never formally studied CS or taken any academic courses in - algorithms, data structures, computer networks, compilers, programming - languages, or other standard CS topics. - -\end_layout - -\begin_layout Standard -A large fraction of successful programmers have no college degrees and perhaps - -\emph on -never -\emph default - studied formally. - They acquired all their knowledge and skills through self-study and practical - work. - A prominent example is Robert C. -\begin_inset space ~ -\end_inset - -Martin -\begin_inset Index idx -status open - -\begin_layout Plain Layout -Robert C. -\begin_inset space ~ -\end_inset - -Martin -\end_layout - -\end_inset - -, -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://en.wikipedia.org/wiki/Robert_C._Martin" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - an outspoken guru in the arts of programming. - He routinely refers to programmers as artisans -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://blog.cleancoder.com/uncle-bob/2013/02/01/The-Humble-Craftsman.html" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - and uses the appropriate imagery: novices and masters, trade and craft, - the honor of the guild, etc. - He compares programmers to plumbers, electricians, lawyers, and surgeons, - but never to mathematicians, physicists, or engineers of any kind. - According to one of his blog posts, -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://blog.cleancoder.com/uncle-bob/2013/11/25/Novices-Coda.html" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - he started working at age 17 as a self-taught programmer. - He never went to college and holds no degrees. -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://hashnode.com/post/i-am-robert-c-martin-uncle-bob-ask-me-anything-cjr7pnh8g000k2cs18o5nhulp/2" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - It is clear that R. -\begin_inset space ~ -\end_inset - -C. -\begin_inset space ~ -\end_inset - -Martin -\emph on -is -\emph default - an expert craftsman and that he did -\emph on -not -\emph default - need academic study to master the craft of programming. -\end_layout - -\begin_layout Standard -Here is another opinion -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://archive.is/tAKQ3" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - (emphasis is theirs): -\end_layout - -\begin_layout Quotation - -\size small -Software Engineering is unique among the STEM careers in that it absolutely - does -\emph on -not -\emph default - require a college degree to be successful. - It most certainly does not require licensing or certification. - -\emph on -It requires experience -\emph default -. -\end_layout - -\begin_layout Standard -This description fits a career in crafts — but certainly not a career, say, - in electrical engineering. -\end_layout - -\begin_layout Standard -The high demand for software developers gave rise to -\begin_inset Quotes eld -\end_inset - -developer boot camps -\begin_inset Quotes erd -\end_inset - - -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://archive.is/GkOL9" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - — vocational schools that educate new programmers in a few months through - purely practical training, with no formal theory or mathematics involved. - These vocational schools are successful -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://archive.is/E9FXP" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - in job placement. - But it is unimaginable that a -\begin_inset Formula $6$ -\end_inset - --month crash course or even a -\begin_inset Formula $2$ -\end_inset - --year vocational school could prepare engineers to work successfully on - designing, say, analog quantum computers -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.dwavesys.com/quantum-computing" - -\end_inset - - -\end_layout - -\end_inset - - without ever learning quantum physics or calculus. -\end_layout - -\begin_layout Subsection -No mathematical formalism guides software development -\end_layout - -\begin_layout Standard -Most books on software engineering contain no formulas or equations, no - mathematical derivations, and no precise definitions of the various technical - terms they are using (such as -\begin_inset Quotes eld -\end_inset - -object-oriented -\begin_inset Quotes erd -\end_inset - - or -\begin_inset Quotes eld -\end_inset - -module's responsibilities -\begin_inset Quotes erd -\end_inset - -). - Some of those books -\begin_inset Foot -status open - -\begin_layout Plain Layout -E.g., -\family typewriter - -\begin_inset CommandInset href -LatexCommand href -target "https://www.amazon.com/Object-Oriented-Software-Engineering-Unified-Methodology/dp/0073376256" - -\end_inset - - -\end_layout - -\end_inset - - also have almost no program code in them. - Some of those books are written by practitioners such as R. -\begin_inset space \space{} -\end_inset - -C. -\begin_inset space \space{} -\end_inset - -Martin never studied any formalisms and do not think in terms of formalisms. - Instead, they summarize their programming experience in vaguely formulated - heuristic “principles”. -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://blog.cleancoder.com/uncle-bob/2016/03/19/GivingUpOnTDD.html" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - The programmers are told: -\begin_inset Quotes eld -\end_inset - -code is about detail -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - -never abandon the big picture -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - -avoid tight coupling in your modules -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - -a class must serve a single responsibility -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - -strive for good interfaces -\begin_inset Quotes erd -\end_inset - -, etc. - -\end_layout - -\begin_layout Standard -In contrast, textbooks on mechanical or electrical engineering include a - significant amount of mathematics. - The design of a microwave antenna is guided not by an -\begin_inset Quotes eld -\end_inset - -open and closed module principle -\begin_inset Quotes erd -\end_inset - - but by solving the relevant differential equations -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://youtu.be/8KpfVsJ5Jw4?t=447" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - of electrodynamics. -\end_layout - -\begin_layout Standard -Another example of programmers' avoidance of mathematical tools is given - by the -\begin_inset Quotes eld -\end_inset - -Liskov substitution principle -\begin_inset Index idx -status open - -\begin_layout Plain Layout -Liskov substitution principle -\end_layout - -\end_inset - - -\begin_inset Quotes erd -\end_inset - - for subtyping -\begin_inset Index idx -status open - -\begin_layout Plain Layout -subtyping -\end_layout - -\end_inset - -. -\begin_inset Foot -status open - -\begin_layout Plain Layout -See -\family typewriter - -\begin_inset CommandInset href -LatexCommand href -target "https://en.wikipedia.org/wiki/Liskov_substitution_principle" -literal "false" - -\end_inset - - -\family default -. - The LSP always holds in functional programming if values are immutable - and subtyping is viewed as an automatic type conversion function (see Definitio -n -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Definition-subtyping" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -). - A property -\begin_inset Formula $\phi(y)$ -\end_inset - - is rewritten as -\begin_inset Formula $\phi(c(y))$ -\end_inset - - by inserting a suitable type conversion function, -\begin_inset Formula $c:S\rightarrow T$ -\end_inset - -. - Since -\begin_inset Formula $c(y)$ -\end_inset - - has type -\begin_inset Formula $T$ -\end_inset - - and -\begin_inset Formula $\phi(x)$ -\end_inset - - holds for all values -\begin_inset Formula $x:T$ -\end_inset - -, we find that the property -\begin_inset Formula $\phi(c(y))$ -\end_inset - - holds automatically. -\end_layout - -\end_inset - - Its rigorous formulation ( -\begin_inset Quotes eld -\end_inset - -for any property -\begin_inset Formula $\phi(x)$ -\end_inset - - that holds for all -\begin_inset Formula $x$ -\end_inset - - of type -\begin_inset Formula $T$ -\end_inset - -, and for any subtype -\begin_inset Formula $S$ -\end_inset - - of -\begin_inset Formula $T$ -\end_inset - -, the property -\begin_inset Formula $\phi(y)$ -\end_inset - - must also hold for all -\begin_inset Formula $y$ -\end_inset - - of type -\begin_inset Formula $S$ -\end_inset - - -\begin_inset Quotes erd -\end_inset - -) is not used by programmers. - Instead, the literature on object-oriented programming formulates the principle - as -\begin_inset Quotes eld -\end_inset - -objects of type -\begin_inset Formula $T$ -\end_inset - - may be substituted by objects of type -\begin_inset Formula $S$ -\end_inset - - while keeping the correctness of the program -\begin_inset Quotes erd -\end_inset - -. - This formulation -\begin_inset Index idx -status open - -\begin_layout Plain Layout -object-oriented programming -\end_layout - -\end_inset - - is both vague (it does not specify how to choose the substituted objects - of type -\begin_inset Formula $S$ -\end_inset - -) and, strictly speaking, incorrect: If the program contains a function - -\begin_inset Formula $f(t)$ -\end_inset - - where -\begin_inset Formula $t$ -\end_inset - - is a value of type -\begin_inset Formula $T$ -\end_inset - -, it is not always possible to find some value -\begin_inset Formula $s$ -\end_inset - - of type -\begin_inset Formula $S$ -\end_inset - - such that -\begin_inset Formula $f(s)=f(t)$ -\end_inset - -. - The reason is that some subtyping relations are not surjective, as shown - in Section -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Subtyping-with-injective" -plural "false" -caps "false" -noprefix "false" - -\end_inset - - of this book. -\end_layout - -\begin_layout Standard -Donald Knuth's classic textbook -\begin_inset Quotes eld -\end_inset - - -\emph on -The Art of Programming -\emph default - -\begin_inset Quotes erd -\end_inset - - indeed treats programming as an art and not as a science. - Knuth shows many algorithms and derives their mathematical properties but - gives almost no examples of realistic program code and does not provide - any theory that could guide programmers in actually -\emph on -writing -\emph default - programs (say, choosing the data types to be used). - Knuth assumes that the reader who understands the mathematical properties - of an algorithm will be able -\emph on -somehow -\emph default - to write correct code. -\end_layout - -\begin_layout Standard -The books -\begin_inset Quotes eld -\end_inset - -The Science of Programming -\begin_inset Quotes erd -\end_inset - - -\begin_inset Foot -status collapsed - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.amazon.com/Science-Programming-Monographs-Computer/dp/0387964800" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - and -\begin_inset Quotes eld -\end_inset - -Program derivation -\begin_inset Quotes erd -\end_inset - - -\begin_inset Foot -status collapsed - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.amazon.com/Program-Derivation-Development-Specifications-International/dp/0201416247" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - are attempts to provide a mathematical basis for writing programs starting - from formal specifications. - The books give some methods that guide programmers in writing code and - at the same time produce a proof that the code conforms to the specification. - However, the scope of proposed methods is limited to designing algorithms - for iterative manipulation of data, such as sorting and searching algorithms. - The procedures suggested in those books are far from a formal mathematical - -\emph on -derivation -\emph default - of a wide range of software programs from specifications. - In any case, most programmers today are unaware of these books and do not - use the methods explained there, even when those methods could apply. -\end_layout - -\begin_layout Standard -Today's computer science courses do not teach a true engineering approach - to software construction. - Some courses teach analysis of programs using mathematical methods. - Two such methods are complexity analysis -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.cs.cmu.edu/~adamchik/15-121/lectures/Algorithmic%20Complexity/complexity.html" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - (the -\begin_inset Quotes eld -\end_inset - -big- -\begin_inset Formula $O$ -\end_inset - - notation -\begin_inset Quotes erd -\end_inset - -) and formal verification. -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://en.wikipedia.org/wiki/Formal_verification" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - But programs are analyzed or verified only -\emph on -after -\emph default - they are somehow written. - Theory does not guide the actual -\emph on -process -\emph default - of writing code: it does not define good ways of organizing the code (e.g., - how to decompose the code into modules, classes, or functions) and does - not tell programmers which data structures and type signatures of functions - will be useful to implement. - Programmers make these design decisions on the basis of experience and - intuition, trial-and-error, copy-paste, guesswork, and debugging. - -\end_layout - -\begin_layout Standard -In a sense, program analysis and verification is analogous to writing mathematic -al equations for the surface of a shoe made by a fashion designer. - The resulting -\begin_inset Quotes eld -\end_inset - -shoe equations -\begin_inset Quotes erd -\end_inset - - are mathematically rigorous and can be analyzed or -\begin_inset Quotes eld -\end_inset - -verified -\begin_inset Quotes erd -\end_inset - -. - But the equations are merely written after the fact, they do not guide - the fashion designers in actually making shoes. - It is understandable that fashion designers do not study the mathematical - theory of surfaces. -\end_layout - -\begin_layout Subsection -Programmers avoid academic terminology -\end_layout - -\begin_layout Standard -Programmers jokingly grumble about terms such as -\begin_inset Quotes eld -\end_inset - -functor -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - -monad -\begin_inset Quotes erd -\end_inset - -, or -\begin_inset Quotes eld -\end_inset - -lambda-functions -\begin_inset Quotes erd -\end_inset - -: -\end_layout - -\begin_layout Quote - -\size small -Those fancy words used by functional programmers purists really annoy me. - Monads, functors... - Nonsense!!! -\size default - -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://archive.is/65K3D" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -Perhaps only a small minority of software developers complain about this, - as the majority seems to be unaware of -\begin_inset Quotes eld -\end_inset - -applicative functors -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - -free monads -\begin_inset Quotes erd -\end_inset - -, and other arcane terminology. - Indeed, that sort of terminology is intentionally avoided by most books - and tutorials aimed at programmers. -\end_layout - -\begin_layout Standard -But why would a software -\emph on -engineer -\emph default - wince at -\begin_inset Quotes eld -\end_inset - -functors -\begin_inset Quotes erd -\end_inset - - or at having to verify the laws of a -\begin_inset Quotes eld -\end_inset - -monad -\begin_inset Quotes erd -\end_inset - -? Other branches of engineering use lots of terminology that is far from - self-explanatory and requires some study. - Chemical engineers learn about -\begin_inset Quotes eld -\end_inset - -Gibbs free energy -\begin_inset Quotes erd -\end_inset - -, which is a technical term that denotes a certain function. - (It does not mean getting energy from J. -\begin_inset space ~ -\end_inset - -W. -\begin_inset space ~ -\end_inset - -Gibbs -\begin_inset Index idx -status open - -\begin_layout Plain Layout -Josiah Willard Gibbs -\end_layout - -\end_inset - - for free.) Chemical engineers accept the need for studying -\begin_inset Quotes eld -\end_inset - -phase diagrams -\begin_inset Quotes erd -\end_inset - - or -\begin_inset Quotes eld -\end_inset - -Fourier's law -\begin_inset Quotes erd -\end_inset - -. - Electrical engineers do not avoid -\begin_inset Quotes eld -\end_inset - -Fourier transforms -\begin_inset Quotes erd -\end_inset - - or -\begin_inset Quotes eld -\end_inset - -delta functions -\begin_inset Quotes erd -\end_inset - - because those are weird things to say. - Mechanical engineers take it for granted that they need -\begin_inset Quotes eld -\end_inset - -rank 4 tensors -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - -Lagrangians -\begin_inset Quotes erd -\end_inset - -, and -\begin_inset Quotes eld -\end_inset - -non-holonomic constraints -\begin_inset Quotes erd -\end_inset - -. - The arcane terminology seems to be the least of their difficulties, as - their textbooks are full of complicated equations and long derivations. -\end_layout - -\begin_layout Standard -Textbooks on true software engineering would have been full of equations - and derivations, teaching engineers how to perform certain calculations - that are required -\emph on -before -\emph default - starting to write code. -\end_layout - -\begin_layout Section -Towards true engineering in software -\end_layout - -\begin_layout Standard -It is now clear that we do not presently have true software engineering. - The people employed under that job title are actually artisans. - They work using artisanal methods, and their culture and processes are - that of a crafts guild. -\end_layout - -\begin_layout Standard -True software engineering means having a mathematical theory that guides - the process of writing programs, — not just theory that describes or analyzes - programs after they are -\emph on -somehow -\emph default - written. -\end_layout - -\begin_layout Standard -It is not enough that the numerical methods required for physics or the - matrix calculations required for data science are -\begin_inset Quotes eld -\end_inset - -mathematical -\begin_inset Quotes erd -\end_inset - -. - These programming tasks are indeed formulated using mathematical theory. - However, mathematical -\emph on -subject matter -\emph default - (aerospace control, physics or astronomy simulations, or statistics) does - not mean that mathematics is used to guide the process of writing code. - Data scientists, aerospace engineers, and physicists almost always work - as artisans when converting their computations into program code. -\end_layout - -\begin_layout Standard -We expect that software engineers -\family sans -' -\family default - textbooks should be full of equations and derivations. - What theory would those equations represent? -\end_layout - -\begin_layout Standard -This theory is what this book calls -\series bold -applied functional type theory -\series default - -\begin_inset Index idx -status open - -\begin_layout Plain Layout -applied functional type theory -\end_layout - -\end_inset - - (see Chapter -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "chap:Applied-functional-type" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -). - It represents the mathematical foundation of the modern practice of functional - programming, as implemented in languages such as OCaml, Haskell, and Scala. - This theory is a blend of set theory, category theory, and logical proof - theory, adapted for the needs of programmers. - It has been in development since late 1990s and is still being actively - worked on by a community of software practitioners and academic computer - scientists. -\end_layout - -\begin_layout Standard -To appreciate that functional programming, unlike any other programming - paradigm, is based on a -\emph on -theory that guides coding -\emph default -, we can look at some recent software engineering conferences such as -\begin_inset Quotes eld -\end_inset - -Scala By the Bay -\begin_inset Quotes erd -\end_inset - - -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://2015.scala.bythebay.io/" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - or BayHac, -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://bayhac.org/" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - or at the numerous FP-related online tutorials and blogs. - We cannot fail to notice that speakers devote significant time to a peculiar - kind of applied mathematical reasoning. - Rather than focusing on one or another API or algorithm, as it is often - the case with other software engineering blogs or presentations, an FP - speaker describes a -\emph on -mathematical structure -\emph default - — such as the -\begin_inset Quotes eld -\end_inset - -applicative functor -\begin_inset Quotes erd -\end_inset - - -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://www.youtube.com/watch?v=bmIxIslimVY" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - or the -\begin_inset Quotes eld -\end_inset - -free monad -\begin_inset Quotes erd -\end_inset - - -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://www.youtube.com/watch?v=U0lK0hnbc4U" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - — and illustrates its use for practical coding. -\end_layout - -\begin_layout Standard -These people are not graduate students showing off their theoretical research. - They are practitioners, software engineers who use FP on their jobs. - It is just the nature of FP that certain mathematical tools and constructions - are directly applicable to practical programming tasks. -\end_layout - -\begin_layout Standard -These mathematical tools are not mere tricks for a specific programming - language; they apply equally to all FP languages. - Before starting to write code, the programmer can jot down certain calculations - in a mathematical notation (see Fig. -\begin_inset space \space{} -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:Example-calculation-in-type-theory" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -). - The results of those calculations will help design the code fragment the - programmer is about to write. - This activity is similar to that of an engineer who performs some mathematical - calculations before embarking on a design project. - -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "50text%" -status open - -\begin_layout Plain Layout -\align center - -\size footnotesize -\begin_inset VSpace 25baselineskip% -\end_inset - - -\begin_inset Graphics - filename ftt-example.jpg - width 100line% - -\end_inset - - -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout - -\size footnotesize -\begin_inset Caption Standard - -\begin_layout Plain Layout -A programmer performs a derivation before writing Haskell code. -\begin_inset CommandInset label -LatexCommand label -name "fig:Example-calculation-in-type-theory" - -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -A recent example of a development in applied functional type theory is the - -\begin_inset Quotes eld -\end_inset - -free applicative functor -\begin_inset Quotes erd -\end_inset - - construction. - It was first described in a 2014 paper; -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://arxiv.org/pdf/1403.0749.pdf" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - a couple of years later, a combined free applicative / free monad data - type was designed and its implementation proposed in Scala -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://github.com/typelevel/cats/issues/983" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - as well as in Haskell. -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://elvishjerricco.github.io/2016/04/08/applicative-effects-in-free-monads.html" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - This technique allows programmers to implement declarative side-effect - computations where some parts are sequential but other parts are computed - in parallel, and to achieve the parallelism -\emph on -automatically -\emph default - while maintaining the composability of the resulting programs. - The new technique has advantages over monad transformers, which was a previousl -y known method of composing declarative side-effects. - The combined -\begin_inset Quotes eld -\end_inset - -free applicative / free monad -\begin_inset Quotes erd -\end_inset - - code was designed and implemented by true software engineers. - They first derived the type constructor that has the necessary algebraic - properties. - Guided by the resulting type formula, they wrote code that was guaranteed - to work as intended. -\end_layout - -\begin_layout Standard -Another example of using applied functional type theory is the -\begin_inset Quotes eld -\end_inset - - -\begin_inset Index idx -status open - -\begin_layout Plain Layout -tagless final -\end_layout - -\end_inset - -tagless final -\begin_inset Quotes erd -\end_inset - - encoding of effects, first described -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://okmij.org/ftp/tagless-final/index.html" - -\end_inset - - -\end_layout - -\end_inset - - in 2009. - That technique (called -\begin_inset Quotes eld -\end_inset - -Church-encoded free monad -\begin_inset Index idx -status open - -\begin_layout Plain Layout -free monad -\end_layout - -\end_inset - - -\begin_inset Quotes erd -\end_inset - - in the present book) has advantages over the ordinary free monad in a number - of cases — just as the free monad itself was used to cure certain problems - with monad transformers. -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://blog.ezyang.com/2013/09/if-youre-using-lift-youre-doing-it-wrong-probably/" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - The new encoding is not tied to a specific programming language. - Rather, it is a language-agnostic construction that was originally described - in OCaml and later used in Haskell and Scala, but can be made to work even - in Java, -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://oleksandrmanzyuk.wordpress.com/2014/06/18/" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - which is not an FP language. -\end_layout - -\begin_layout Standard -This example shows that we may need several more years of work before the - practical aspects of using applied functional type theory are sufficiently - well understood by the FP community. - The theory is in active development, and its design patterns — as well - as the exact scope of the requisite theoretical material — are still being - figured out. - If the 40-year gap hypothesis -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.linkedin.com/pulse/40-year-gap-what-has-academic-computer-science-ever-done-winitzki" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - holds, we should expect applied functional type theory (perhaps under a - different name) to become mainstream by 2030. - This book is a step towards a clear designation of the scope of that theory. -\end_layout - -\begin_layout Section -Does software need engineers, or are artisans sufficient? -\end_layout - -\begin_layout Standard -The demand for programmers is growing. - -\begin_inset Quotes eld -\end_inset - -Software developer -\begin_inset Quotes erd -\end_inset - - was #1 best job -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://money.usnews.com/money/careers/articles/how-us-news-ranks-the-best-jobs" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - in the US in 2018. - But is there a demand for engineers or just for artisans? -\end_layout - -\begin_layout Standard -We do not seem to be able to train enough software artisans. -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://archive.is/137b8" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - So, it is probably impossible to train as many software engineers in the - true sense of the word. - Modern computer science courses do not actually train engineers in that - sense. - Instead, they train academic researchers who can also work as software - artisans and write code. -\end_layout - -\begin_layout Standard -Looking at the situation in construction business in the U.S.A., we find that - it employs about -\begin_inset Formula $10$ -\end_inset - - times more construction workers as architects. - We might conclude that perhaps one software engineer is required per dozen - software artisans. -\end_layout - -\begin_layout Standard -What is the price of -\emph on -not -\emph default - having engineers, of replacing them with artisans? -\end_layout - -\begin_layout Standard -Software practitioners have long bemoaned the permanent state of -\begin_inset Quotes eld -\end_inset - -crisis -\begin_inset Quotes erd -\end_inset - - in software development. - Code -\begin_inset Quotes eld -\end_inset - -rots with time -\begin_inset Quotes erd -\end_inset - -, its complexity grows -\begin_inset Quotes eld -\end_inset - -out of control -\begin_inset Quotes erd -\end_inset - -, and operating systems have been notorious for ever-appearing security - flaws -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://archive.fo/HtQzw" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - despite many thousands of programmers and testers employed. - It appears that the growing complexity of software tends to overwhelm the - capacity of the human brain for correct -\emph on -artisanal -\emph default - programming. -\end_layout - -\begin_layout Standard -It is precisely in designing large and robust software systems that we would - benefit from true engineering. - Artisans has been building bridges and using chemical reactions by trial - and error and via tradition, long before mechanical or chemical engineering - disciplines were developed and founded upon rigorous theory. - But once the theory became available, engineers were able to design unimaginabl -y more powerful and complicated structures, devices, and processes. - So, we may expect that trial, error, and adherence to tradition is inadequate - for some of the more complex software development tasks in front of us. - -\end_layout - -\begin_layout Standard -To build large and reliable software, such as new mobile or embedded operating - systems or distributed peer-to-peer trust architectures, we will most likely - need the qualitative increase in productivity and reliability that can - only come from replacing artisanal programming by a true engineering discipline. - Applied functional type theory and functional programming are steps in - that direction. -\end_layout - -\begin_layout Chapter -Essay: Towards functional data engineering with Scala -\end_layout - -\begin_layout Standard -Data engineering is among the highest-demand -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "http://archive.is/mK59h" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - novel occupations in the IT world today. - Data engineers create software pipelines that process large volumes of - data efficiently. - Why did the Scala programming language emerge as a premier tool -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.slideshare.net/noootsab/scala-the-unpredicted-lingua-franca-for-data-science" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - for crafting the foundational data engineering technologies such as Spark - or Akka? Why is Scala in high demand -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://techcrunch.com/2016/06/14/scala-is-the-new-golden-child/" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - within the world of big data? -\end_layout - -\begin_layout Standard -There are reasons to believe that the choice of Scala was not accidental. -\end_layout - -\begin_layout Section -Data is math -\end_layout - -\begin_layout Standard -Humanity has been working with data at least since Babylonian tax tables -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.nytimes.com/2017/08/29/science/trigonometry-babylonian-tablet.html" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - and the ancient Chinese number books. -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -name "https://quatr.us/china/science/chinamath.htm" -target "https://web.archive.org/web/20170425233550/https://quatr.us/china/science/chinamath.htm" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - Mathematics summarizes several millennia's worth of data processing experience - in a few fundamental tenets: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement I -overhang 0in -width "34col%" -status open - -\begin_layout Plain Layout -\align center -\begin_inset VSpace -65baselineskip% -\end_inset - - -\begin_inset Graphics - filename type-error.jpg - width 96line% - -\end_inset - - -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout -\begin_inset Caption Standard - -\begin_layout Plain Layout -\begin_inset Index idx -status open - -\begin_layout Plain Layout -jokes -\end_layout - -\end_inset - -Mixing incompatible data types produces nonsensical results. -\begin_inset CommandInset label -LatexCommand label -name "fig:A-nonsensical-calculation" - -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -350baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Itemize -Data is -\emph on -immutable -\emph default -, because facts are immutable. - -\end_layout - -\begin_layout Itemize -Each -\emph on -type -\emph default - of values (population count, land area, distance, price, location, time, - etc.) needs to be handled separately; it is meaningless to add a distance - to a population count. -\end_layout - -\begin_layout Itemize -Data processing should be performed according to -\emph on -mathematical formulas -\emph default -. - -\end_layout - -\begin_layout Standard -Violating these tenets produces nonsense (see Fig. -\begin_inset space \space{} -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:A-nonsensical-calculation" -plural "false" -caps "false" -noprefix "false" - -\end_inset - - for a real-life illustration). -\end_layout - -\begin_layout Standard -The power of the principles of mathematics extends over all epochs and all - cultures; math is the same in San Francisco, in Rio de Janeiro, in Kuala-Lumpur -, and in Pyongyang (Fig. -\begin_inset space \space{} -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:The-Pyongyang-method-of-error-free-programming" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -). -\end_layout - -\begin_layout Section -Functional programming is math -\end_layout - -\begin_layout Standard -The functional programming paradigm is based on mathematical principles: - values are immutable, data processing is coded through formula-like expressions -, and each type of data is required to match correctly during the computations. - The type-checking process automatically prevents programmers from making - many kinds of coding errors. - In addition, programming languages such as Scala and Haskell have a set - of features adapted to building powerful abstractions and domain-specific - languages. - This power of abstraction is not accidental. - Since mathematics is the ultimate art of building abstractions, math-based - functional programming languages capitalize on having several millennia - of mathematical experience. -\end_layout - -\begin_layout Standard -A prominent example of how mathematics informs the design of programming - languages is the connection between constructive logic -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://en.wikipedia.org/wiki/Intuitionistic_logic" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - and the programming language's type system, called the Curry-Howard (CH) - correspondence. - The main idea of the CH correspondence -\begin_inset Index idx -status open - -\begin_layout Plain Layout -Curry-Howard correspondence -\end_layout - -\end_inset - - is to think of programs as mathematical formulas that compute a value of - a certain type -\begin_inset Formula $A$ -\end_inset - -. - The CH correspondence is between programs and logical propositions: To - any program that computes a value of type -\begin_inset Formula $A$ -\end_inset - -, there corresponds a proposition stating that -\begin_inset Quotes eld -\end_inset - -a value of type -\begin_inset Formula $A$ -\end_inset - - can be computed -\begin_inset Quotes erd -\end_inset - -. -\end_layout - -\begin_layout Standard -This may sound rather theoretical so far. - To see the real value of the CH correspondence, recall that formal logic - has operations -\begin_inset Quotes eld -\end_inset - - -\series bold -\emph on -and -\series default -\emph default - -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - - -\series bold -\emph on -or -\series default -\emph default - -\begin_inset Quotes erd -\end_inset - -, and -\begin_inset Quotes eld -\end_inset - - -\series bold -\emph on -implies -\series default -\emph default - -\begin_inset Quotes erd -\end_inset - -. - For any two propositions -\begin_inset Formula $A$ -\end_inset - -, -\begin_inset Formula $B$ -\end_inset - -, we can construct the propositions -\begin_inset Quotes eld -\end_inset - - -\begin_inset Formula $A$ -\end_inset - - -\series bold -\emph on -and -\series default -\emph default - -\begin_inset Formula $B$ -\end_inset - - -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - - -\begin_inset Formula $A$ -\end_inset - - -\series bold -\emph on -or -\series default -\emph default - -\begin_inset Formula $B$ -\end_inset - - -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - - -\begin_inset Formula $A$ -\end_inset - - -\series bold -\emph on -implies -\series default -\emph default - -\begin_inset Formula $B$ -\end_inset - - -\begin_inset Quotes erd -\end_inset - -. - These three logical operations are foundational; without one of them, the - logic is -\emph on -incomplete -\emph default - (you cannot derive some theorems). -\end_layout - -\begin_layout Standard -A programming language -\series bold -obeys the CH correspondence -\series default - -\begin_inset Index idx -status open - -\begin_layout Plain Layout -Curry-Howard correspondence -\end_layout - -\end_inset - - with the logic if for any types -\begin_inset Formula $A$ -\end_inset - -, -\begin_inset Formula $B$ -\end_inset - -, the language also contains composite types corresponding to the logical - formulas -\begin_inset Quotes eld -\end_inset - - -\begin_inset Formula $A$ -\end_inset - - -\series bold -\emph on -or -\series default -\emph default - -\begin_inset Formula $B$ -\end_inset - - -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - - -\begin_inset Formula $A$ -\end_inset - - -\series bold -\emph on -and -\series default -\emph default - -\begin_inset Formula $B$ -\end_inset - - -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - - -\begin_inset Formula $A$ -\end_inset - - -\series bold -\emph on -implies -\series default -\emph default - -\begin_inset Formula $B$ -\end_inset - - -\begin_inset Quotes erd -\end_inset - -. - In Scala, these composite types are -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Either[A, B] -\end_layout - -\end_inset - -, the tuple -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -(A,B) -\end_layout - -\end_inset - -, and the function type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -A => B -\end_layout - -\end_inset - -. - All modern functional languages such as OCaml, Haskell, Scala, F#, Swift, - Elm, and PureScript support these three type constructions and thus obey - the CH correspondence. - Having a -\emph on -complete -\emph default - logic in a language's type system enables declarative domain-driven code - design. -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://fsharpforfunandprofit.com/ddd/" -literal "false" - -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement I -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\align center -\begin_inset VSpace -50baselineskip% -\end_inset - - -\begin_inset Graphics - filename no-bugs.jpg - width 100line% - -\end_inset - - -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout -\begin_inset Caption Standard - -\begin_layout Plain Layout -\begin_inset Index idx -status open - -\begin_layout Plain Layout -jokes -\end_layout - -\end_inset - -The Pyongyang method of error-free software engineering. -\begin_inset CommandInset label -LatexCommand label -name "fig:The-Pyongyang-method-of-error-free-programming" - -\end_inset - - -\end_layout - -\end_inset - - -\begin_inset VSpace -300baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -It is interesting to note that most older programming languages (C/C++, - Java, JavaScript, Python) do not support some of these composite types. - In other words, these programming languages have type systems based on - an incomplete logic. - As a result, users of these languages have to implement burdensome workarounds - that make for error-prone code. - Failure to follow mathematical principles has real costs (Figure -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:The-Pyongyang-method-of-error-free-programming" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -). -\end_layout - -\begin_layout Section -The power of abstraction -\end_layout - -\begin_layout Standard -Early adopters of Scala, such as Netflix, LinkedIn, and Twitter, were implementi -ng what is now called -\begin_inset Quotes eld -\end_inset - -big data engineering -\begin_inset Quotes erd -\end_inset - -. - The required software needs to be highly concurrent, distributed, and resilient - to failure. - Those software companies used Scala as their main implementation language - and reaped the benefits of functional programming. -\end_layout - -\begin_layout Standard -What makes Scala suitable for big data tasks? The only reliable way of managing - massively concurrent code is to use sufficiently high-level abstractions - that make application code declarative. - The two most important such abstractions are the -\begin_inset Quotes eld -\end_inset - -resilient distributed dataset -\begin_inset Quotes erd -\end_inset - - (RDD) of Apache Spark and the -\begin_inset Quotes eld -\end_inset - -reactive stream -\begin_inset Quotes erd -\end_inset - - used in systems such as Kafka, Akka Streams, and Apache Flink. - While these abstractions are certainly implementable in Java or Python, - a fully declarative and type-safe usage is possible only in a programming - language with a sophisticated type system. - Among the currently available mature functional languages, only Scala and - Haskell are technically adequate for that task, due to their support for - typeclasses and higher-order types. - The early adopters of Scala were able to benefit from the powerful abstractions - Scala supports. - In this way, Scala enabled those businesses to engineer and to scale up - their massively concurrent computations. -\end_layout - -\begin_layout Standard -It remains to see why Scala (and not, say, OCaml or Haskell) became the - -\emph on -lingua franca -\emph default - of big data. -\end_layout - -\begin_layout Section -Scala is Java on math -\end_layout - -\begin_layout Standard -The recently invented general-purpose functional programming languages may - be divided into -\begin_inset Quotes eld -\end_inset - -academic -\begin_inset Quotes erd -\end_inset - - (OCaml, Haskell) and -\begin_inset Quotes eld -\end_inset - -industrial -\begin_inset Quotes erd -\end_inset - - (F#, Scala, Swift). -\end_layout - -\begin_layout Standard -The -\begin_inset Quotes eld -\end_inset - -academic -\begin_inset Quotes erd -\end_inset - - languages are clean-room implementations of well-researched mathematical - principles of programming language design (the CH correspondence being - one such principle). - These languages are not limited by requirements of compatibility with any - existing platforms or libraries. - Because of this, the -\begin_inset Quotes eld -\end_inset - -academic -\begin_inset Quotes erd -\end_inset - - languages have been designed and used for pursuing various mathematical - ideas to their logical conclusion. -\begin_inset Foot -status open - -\begin_layout Plain Layout -OCaml has arbitrary recursive product and co-product types that can be freely - combined with object-oriented types. - Haskell removes all side effects from the language and supports partial - type functions of arbitrarily high order. -\end_layout - -\end_inset - - At the same time, software practitioners struggle to adopt these programming - languages due to a steep learning curve, a lack of enterprise-grade libraries - and tool support, and immature package management. -\end_layout - -\begin_layout Standard -The languages from the -\begin_inset Quotes eld -\end_inset - -industrial -\begin_inset Quotes erd -\end_inset - - group are based on existing and mature software ecosystems: F# on .NET, - Scala on JVM, and Swift on the MacOS/iOS platform. - One of the important design requirements for these languages is 100% binary - compatibility with their -\begin_inset Quotes eld -\end_inset - -parent -\begin_inset Quotes erd -\end_inset - - platforms and languages (F# with C#, Scala with Java, and Swift with Objective- -C). - Because of this, developers can immediately take advantage of the existing - tooling, package management, and industry-strength libraries, while slowly - ramping up the idiomatic usage of new language features. - However, the same compatibility requirements dictate certain limitations - in the languages, making their design less than fully satisfactory from - the functional programming viewpoint. -\end_layout - -\begin_layout Standard -It is now easy to see why the adoption rate of the -\begin_inset Quotes eld -\end_inset - -industrial -\begin_inset Quotes erd -\end_inset - - group of languages is much higher -\begin_inset Foot -status open - -\begin_layout Plain Layout - -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.tiobe.com/tiobe-index/" -literal "false" - -\end_inset - - -\family default -, archived in 2019 at -\family typewriter - -\begin_inset CommandInset href -LatexCommand href -target "http://archive.is/RsNH8" - -\end_inset - - -\end_layout - -\end_inset - - than that of the -\begin_inset Quotes eld -\end_inset - -academic -\begin_inset Quotes erd -\end_inset - - languages. - The transition to the functional paradigm is also smoother for software - developers because F#, Scala, and Swift seamlessly support the familiar - object-oriented programming -\begin_inset Index idx -status open - -\begin_layout Plain Layout -object-oriented programming -\end_layout - -\end_inset - - paradigm. - At the same time, these new languages still have logically complete type - systems, which gives developers an important benefit of type-safe domain - modeling. -\end_layout - -\begin_layout Standard -Nevertheless, the type systems of these languages are not equally powerful. - For instance, F# and Swift are similar to OCaml in many ways but omit OCaml's - parameterized modules and some other features. - Of all mentioned languages, only Scala and Haskell directly support typeclasses - and higher-order functions on types, which are helpful for expressing abstracti -ons such as automatically parallelized data sets or asynchronous data streams. -\end_layout - -\begin_layout Standard -To see the impact of these advanced features, consider LINQ, a domain-specific - language for database queries on .NET, implemented in C# and F# through - a special built-in syntax supported by Microsoft's compilers. - Analogous functionality is provided in Scala as a -\emph on -library -\emph default -, without need to modify the Scala compiler, by several open-source projects - such as Slick and Quill. - Similar libraries exist for Haskell — but not in languages with less powerful - type systems. -\end_layout - -\begin_layout Section -Summary -\end_layout - -\begin_layout Standard -Only Scala has all of the features required for industrial-grade functional - programming: -\end_layout - -\begin_layout Enumerate -Functional collections in the standard library. -\end_layout - -\begin_layout Enumerate -A sophisticated type system with support for typeclasses and higher-order - types. -\end_layout - -\begin_layout Enumerate -Seamless compatibility with a mature software ecosystem (JVM). -\end_layout - -\begin_layout Standard -Based on this assessment, we may be confident in Scala's future as a main - implementation language for big data engineering. -\end_layout - -\begin_layout Chapter -Essay: Why category theory is useful in functional programming -\end_layout - -\begin_layout Standard -\begin_inset Index idx -status collapsed - -\begin_layout Plain Layout -category theory!in functional programming -\end_layout - -\end_inset - -This essay is for readers who are already somewhat familiar with category - theory. -\end_layout - -\begin_layout Section -A -\begin_inset Quotes eld -\end_inset - -types/functions -\begin_inset Quotes erd -\end_inset - - category for a programming language -\end_layout - -\begin_layout Standard -We consider programming languages that support various data types, such - as integers ( -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Int -\end_layout - -\end_inset - -), floating-point numbers ( -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Float -\end_layout - -\end_inset - -), strings ( -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -String -\end_layout - -\end_inset - -), arrays of strings ( -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Array[String] -\end_layout - -\end_inset - -), and so on. - Such languages allow programmers to define functions with specified types - of arguments and return values. - The compiler will then verify that all functions are always applied to - arguments of correct types, and also that all return values have the expected - types. - -\end_layout - -\begin_layout Standard -To each programming language of that kind, there corresponds a + a \begin_inset Quotes eld \end_inset @@ -5953,7 +2875,7 @@ FP languages , such as OCaml, Haskell, Scala and others). \end_layout -\begin_layout Section +\begin_layout Addsec The use of endofunctors \end_layout @@ -6397,7 +3319,7 @@ fmap will work according to the programmer's intuitive expectations. \end_layout -\begin_layout Section +\begin_layout Addsec The use of natural transformations \end_layout @@ -6702,7 +3624,7 @@ reverse[X] from the powerful reasoning tools of category theory. \end_layout -\begin_layout Section +\begin_layout Addsec Other properties of the \begin_inset Quotes eld \end_inset @@ -6859,7 +3781,7 @@ types/functions proved useful in the practice of software engineering. \end_layout -\begin_layout Section +\begin_layout Addsec Some useful sub-categories of endofunctors \end_layout @@ -7025,7 +3947,7 @@ fmap \end_layout -\begin_layout Subsection +\begin_layout Subsection* Filterable endofunctors \end_layout @@ -7448,7 +4370,7 @@ takeWhile . \end_layout -\begin_layout Subsection +\begin_layout Subsection* Monadic endofunctors \end_layout @@ -7785,7 +4707,7 @@ types/functions category. \end_layout -\begin_layout Section +\begin_layout Addsec Category theory and the laws of FP idioms \end_layout diff --git a/sofp-src/sofp-essay3.pre.md b/sofp-src/sofp-essay3.pre.md new file mode 100644 index 000000000..e69de29bb diff --git a/sofp-src/sofp-essays.tex b/sofp-src/sofp-essays.tex deleted file mode 100644 index 1f409b657..000000000 --- a/sofp-src/sofp-essays.tex +++ /dev/null @@ -1,1352 +0,0 @@ - -\chapter{\textquotedblleft Applied functional type theory\textquotedblright : -A proposal\label{chap:Applied-functional-type}} - -What exactly is the extent of \textsf{``}theory\textsf{''} that a software engineer -should know in order to be a proficient functional programmer? This -book proposes an answer to that question by presenting a coherent -body of theoretical knowledge that, in the author\textsf{'}s view, \emph{is} -the theory that underlies the practice of functional programming and -guides software engineers in writing code. This body of knowledge -may be viewed as a new emerging sub-branch of computer science, tentatively -called \textbf{\index{applied functional type theory}applied functional -type theory} (AFTT). - -In order to discover the proper scope of AFTT, this book appraises -the various inventions made in the field of functional programming -in the last 30 years, such as the \textquotedblleft functional pearls\textquotedblright{} -papers\footnote{\texttt{\href{https://wiki.haskell.org/Research_papers/Functional_pearls}{https://wiki.haskell.org/Research\_papers/Functional\_pearls}}} -and various online tutorials, looking for theoretical material that -has demonstrated its pragmatic usefulness. As a first step towards -formulating AFTT from the ground up, the results are presented in -the form of a tutorial, with motivations and rigorous derivations -of substantially all relevant mathematical facts. - -In this book, code examples are written in Scala because the author -is fluent in that language. However, most of the material will work -equally well in Haskell, OCaml, and other FP languages. This is because -AFTT is the science of functional programming and not a set of tricks -specific to Scala or Haskell. An advanced user of any functional programming -language will have to face the same questions and struggle with the -same practical issues. - -\section{AFTT is not covered by courses in computer science} - -Traditional courses of computer science (algorithms and data structures, -complexity theory, distributed systems, databases, network systems, -compilers, operating systems) are largely not relevant to AFTT. Courses -in programming language theory are more relevant but are not presented -at an appropriate level. To an academic computer scientist, the theory -behind Haskell is \textsf{``}System $F\omega$\textsf{''}, a version of $\lambda$-calculus\index{System Fomega (Haskell)@System $F\omega$ (Haskell)}.\footnote{\texttt{\href{https://babel.ls.fi.upm.es/~pablo/Papers/Notes/f-fw.pdf}{https://babel.ls.fi.upm.es/$\sim$pablo/Papers/Notes/f-fw.pdf}}} -That theory guided the design of the Haskell language and defines -rigorously what a Haskell program means in a mathematical sense. The -theory behind Scala is called the \textsf{``}DOT\textsf{''} (dependent object type) -calculus.\footnote{\texttt{\href{https://www.scala-lang.org/blog/2016/02/03/essence-of-scala.html}{https://www.scala-lang.org/blog/2016/02/03/essence-of-scala.html}}}\index{dependent object type (DOT) calculus} -That theory guided the design of Scala version 3. - -However, a practicing Haskell or Scala programmer is not concerned -with designing Haskell or Scala, or with proving theoretical properties -of those languages. Instead, the programmer is mainly concerned with -\emph{using} a chosen programming language to write code. - -Knowing how to prove various properties of System $F\omega$ or DOT -will not actually help programmers to write code. So, these theories -are outside the scope of AFTT. The practice of functional programming -does not require graduate-level theoretical studies. - -As an example of theoretical material that \emph{is} within the scope -of AFTT, consider applicative functors (Chapter~\ref{chap:8-Applicative-functors,-contrafunctors}).\index{applicative functors} -It is helpful for a practicing functional programmer to be able to -recognize and use applicative functors. An applicative functor is -a data structure specifying declaratively some operations that can -run independently of each other. Programs may combine these operations, -execute them in parallel, check for validity, or refactor for optimization -or better maintainability. - -To use this functionality, the programmer must begin by checking whether -a given data structure satisfies the laws of applicative functors. -In a given application, the choice of a data structure may be dictated -in part by the business logic. The programmer first writes down the -type of that data structure and the code implementing the required -methods. The programmer can then check whether the laws hold. The -data structure and the code may need to be adjusted in order to fit -the definition of an applicative functor and to make the laws hold. - -So, the programmer needs to perform a certain amount of symbolic derivations -before coding. The derivations can be done using pen and paper by -writing equations in a concise mathematical notation. Once the laws -are verified, the programmer proceeds to write code. - -The mathematical proofs and derivations assure that the chosen data -structure will satisfy the laws of applicative functors, no matter -how the rest of the program is written. So, for example, it is assured -that the relevant operations can be automatically parallelized and -will still work correctly. In this way, AFTT directly guides the programmer -and helps write correct code. - -Applicative functors were discovered by practitioners who were using -Haskell to implement parser combinators for compilers. However, applicative -functors are not a feature of Haskell. Rather, they are a design pattern -that may be used in Scala or in any other functional programming language. -A prominent example of an applicative functor is Apache Spark\textsf{'}s \lstinline!RDD! -data type, which is widely used for implementing large-scale parallel -computations.\footnote{\texttt{\href{https://spark.apache.org/docs/latest/rdd-programming-guide.html}{https://spark.apache.org/docs/latest/rdd-programming-guide.html}}} -And yet, no standard computer science course or textbook defines applicative -functors, motivates their laws, explores their structure on examples, -or shows data types that are \emph{not} applicative functors (and -explains why not). - -\section{AFTT is not category theory, type theory, or formal logic} - -One often hears that functional programming is based on category theory.\footnote{\texttt{\href{https://www.47deg.com/blog/science-behind-functional-programming/}{https://www.47deg.com/blog/science-behind-functional-programming/}}} -Indeed, the material shown in this book includes a (small) number -of notions from category theory, as well as from formal logic and -type theory. However, software engineers would not benefit from traditional -academic courses in those subjects: their presentation is too abstract -and at the same time lacks specific results necessary for practical -programming. Those courses answer questions that academic mathematicians -have, not questions that practicing functional programmers have. - -There exist books intended as presentations of category theory for -computer scientists\footnote{See, e.g., \texttt{\href{https://www.amazon.com/dp/0262660717}{https://www.amazon.com/dp/0262660717}} -or \texttt{\href{https://www.math.mcgill.ca/triples/Barr-Wells-ctcs.pdf}{https://www.math.mcgill.ca/triples/Barr-Wells-ctcs.pdf}}} or for programmers.\footnote{\texttt{\href{https://github.com/hmemcpy/milewski-ctfp-pdf}{https://github.com/hmemcpy/milewski-ctfp-pdf}}} -However, those books do not cover certain concepts relevant to programming, -such as applicative\footnote{Applicative functors are known in mathematics as \textsf{``}monoidal\textsf{''}: \texttt{\href{https://en.wikipedia.org/wiki/Monoidal_functor}{https://en.wikipedia.org/wiki/Monoidal\_functor}}} -or traversable functors. Instead, those books dwell on concepts (e.g., -limits, enriched categories, topoi) that have no applications in practical -functional programming today. - -Typical questions in academic books are \textsf{``}Is $X$ an introduction -rule or an elimination rule\textsf{''} and \textsf{``}Does property $Y$ hold in non-small -categories or only in the category of sets\textsf{''}. Questions a Scala programmer -might ask are \textsf{``}Can we compute a value of type \lstinline!Either[Z, R => A]! -from a value of type \lstinline!R => Either[Z, A]!\textsf{''} and \textsf{``}Is the -type constructor \lstinline!F[A] = Option[(A,A,A)]! a monad or only -an applicative functor\textsf{''}. The scope of AFTT includes answering the -last two questions but \emph{not} the first two. - -A software engineer hoping to understand the theory behind functional -programming will not find the concepts of filterable, applicative, -or traversable functors in any currently available books on category -theory, including books intended for programmers. And yet these concepts -are necessary for correct implementations of the important and widely -used operations \lstinline!filter!, \lstinline!zip!, and \lstinline!fold!. - -To compensate for the lack of AFTT textbooks, programmers have written -many online tutorials, aiming to explain the theoretical concepts -necessary for practical work. The term \textsf{``}monad tutorial\textsf{''} became -infamous because so many were posted online.\footnote{\texttt{\href{https://www.johndcook.com/blog/2014/03/03/monads-are-hard-because/}{https://www.johndcook.com/blog/2014/03/03/monads-are-hard-because/}}} -Tutorials were also written about applicative functors, traversable -functors, free monads, etc., showing a real unfulfilled need for presenting -practice-relevant fragments of theory in an applied setting. - -For example, free monads became popular in the Scala community around -2015. Many talks about free monads were presented at Scala engineering -conferences, giving different implementations but never formulating -rigorously the properties required of a valid implementation of a -free monad. Without knowing the required mathematical properties of -free monads, a programmer cannot make sure that a given implementation -is correct. However, books on category theory define free monads in -a way that is unsuitable for programming applications (a free monad -is an adjoint functor to a forgetful functor from a Kleisli category -to the category of sets).\footnote{\textsf{``}\emph{A monad is just a monoid in the category of endofunctors. -What\textsf{'}s the problem?}\textsf{''} as the well-known joke\index{jokes} goes.\label{fn:A-monad-is-a-monoid-in-category-of-endofunctors-big-deal} -For background information about that joke, see \texttt{\href{https://stackoverflow.com/questions/3870088/}{https://stackoverflow.com/questions/3870088/}}; -a related joke is in footnote~\ref{fn:Whats-the-big-deal-monad-transformers} -on page~\pageref{fn:Whats-the-big-deal-monad-transformers}.} Such \textsf{``}academic\textsf{''} definitions can be used neither as guidance for -writing code or checking code correctness, nor as a conceptual explanation -that a learner would find helpful. - -Perhaps the best selection of AFTT tutorial material today can be -found in the \textsf{``}Haskell Wikibooks\textsf{''}.\footnote{\texttt{\href{https://en.wikibooks.org/wiki/Haskell}{https://en.wikibooks.org/wiki/Haskell}}} -However, those tutorials are incomplete and limited to explaining -the use of Haskell. Many of them are suitable neither as a first introduction -nor as a reference on AFTT. Also, the Haskell Wikibooks tutorials -rarely show any derivations of laws or explain the required techniques. - -Apart from referring to some notions of category theory, AFTT also -uses concepts from type theory and formal logic. However, existing -textbooks of type theory and logic focus on formal semantics, domain -theory, and proof theory. From a practicing programmer\textsf{'}s viewpoint, -these books present a lot of difficult-to-learn material that will -not help in writing code. At the same time, those academic books never -mention the practical techniques used in many functional programming -libraries today, such as reasoning about and implementing types with -quantifiers, types parameterized by type constructors, or partial -type-to-value functions (known as \textsf{``}typeclasses\textsf{''}). - -The proper scope of AFTT is to help the programmer with practical -tasks such as: -\begin{enumerate} -\item Deciding whether two data types are equivalent and implementing the -isomorphism transformations. For example, the Scala type \lstinline!(A, Either[B, C])! -is equivalent to \lstinline!Either[(A, B), (A, C)]!, but the type -\lstinline!A => Either[B, C]! is \emph{not} equivalent to \lstinline!Either[A => B, A => C]!. -\item Checking whether a definition of a recursive type is \textsf{``}valid\textsf{''}, -i.e., does not lead to infinite loops. A simple example of an \textsf{``}invalid\textsf{''} -recursive type definition in Scala is \lstinline!class Bad(x: Bad)!. -A small change transforms that example into a \textsf{``}valid\textsf{''} recursive -type: \lstinline!class Good(x: Option[Good])!. -\item Deciding whether a function with a given type signature can be implemented. -For example, -\begin{lstlisting} -def f[Z,A,R]: (R => Either[Z, A]) => Either[Z, R => A] = ??? // Cannot be implemented. -def g[Z,A,R]: Either[Z, R => A] => (R => Either[Z, A]) = ??? // Can be implemented. -\end{lstlisting} -\item Deriving an implementation of a function from its type signature and -checking required laws. For example, deriving the \lstinline!flatMap! -method and checking its laws for the \lstinline!Reader! monad: -\begin{lstlisting} -def flatMap[Z, A, B](r: Z => A)(f: A => Z => B): Z => B = ??? -\end{lstlisting} -\item Deriving a simpler but equivalent code by calculating with functions -and laws. -\end{enumerate} -These are real-world applications of type theory and the Curry-Howard -correspondence, but existing books on type theory and logic do not -give practical recipes for performing these tasks.\footnote{Task 5 is addressed in several programming-oriented books such as -\emph{Pearls of functional algorithm design} by \index{Richard Bird}Richard -Bird (\texttt{\href{https://www.cambridge.org/9780521513388}{https://www.cambridge.org/9780521513388}}).} - -Books such as \emph{Scala with Cats},\footnote{\texttt{\href{https://underscore.io/books/scala-with-cats/}{https://underscore.io/books/scala-with-cats/}}} -\emph{Functional programming simplified},\footnote{\texttt{\href{https://alvinalexander.com/scala/functional-programming-simplified-book}{https://alvinalexander.com/scala/functional-programming-simplified-book}}} -and \emph{Functional programming for mortals}\footnote{\texttt{\href{http://www.lulu.com/shop/search.ep?contributorId=1600066}{http://www.lulu.com/shop/search.ep?contributorId=1600066}}} -are primarily focused on explaining practical aspects of functional -programming and do not derive the mathematical laws for, e.g., filterable, -monadic, applicative, or traversable functors. - -The only currently available Scala-based AFTT textbook is \emph{Functional -Programming in Scala}.\footnote{\texttt{\href{https://www.manning.com/books/functional-programming-in-scala}{https://www.manning.com/books/functional-programming-in-scala}}} -It balances practical coding with theoretical developments and laws. -\emph{Program design by calculation}\footnote{\texttt{\href{http://www4.di.uminho.pt/~jno/ps/pdbc.pdf}{http://www4.di.uminho.pt/$\sim$jno/ps/pdbc.pdf}}} -is another (Haskell-oriented) AFTT book in progress. The present book -is written at about the same level but aims at better motivation for -mathematical concepts and a wider range of pedagogical examples that -help build the necessary intuition and facility with the techniques -of formal derivation. - -Figures~\ref{fig:Randomly-chosen-pages-1}\textendash \ref{fig:Randomly-chosen-pages} -illustrate the difference between AFTT books, programming books, and -academic science books, by showing randomly chosen pages from such -books. One gets a visual impression that programming-oriented books -contain code examples and explanations in words but no formal derivations. -Books on AFTT, as well as books on mathematics and science, will typically -show equations, diagrams, and derivations. The present book contains -both code examples and mathematical proofs. - -\begin{figure} -\begin{centering} -\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpsimplified-pdf-00}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpsimplified-pdf-01}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpsimplified-pdf-02}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpsimplified-pdf-03}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpsimplified-pdf-04}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpsimplified-pdf-05}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpsimplified-pdf-06}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpsimplified-pdf-07} -\par\end{centering} -\begin{centering} -\vspace{-0.3\baselineskip} -\par\end{centering} -\begin{centering} -\emph{Functional programming simplified}, by A.~Alexander -\par\end{centering} -\begin{centering} -\vspace{1\baselineskip} -\par\end{centering} -\begin{centering} -\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpmortals-pdf-00}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpmortals-pdf-01}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpmortals-pdf-02}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpmortals-pdf-03}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpmortals-pdf-04}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpmortals-pdf-06}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpmortals-pdf-07}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpmortals-pdf-08} -\par\end{centering} -\begin{centering} -\emph{\vspace{-1.6\baselineskip} -} -\par\end{centering} -\begin{centering} -\emph{Functional programming for mortals}, by S.~Halliday -\par\end{centering} -\begin{centering} -\vspace{1\baselineskip} -\par\end{centering} -\begin{centering} -\includegraphics[height=2.51cm]{random-pages/random-pages-from-volpe-pdf-01}\includegraphics[height=2.51cm]{random-pages/random-pages-from-volpe-pdf-03}\includegraphics[height=2.51cm]{random-pages/random-pages-from-volpe-pdf-04}\includegraphics[height=2.51cm]{random-pages/random-pages-from-volpe-pdf-05}\includegraphics[height=2.51cm]{random-pages/random-pages-from-volpe-pdf-06}\includegraphics[height=2.51cm]{random-pages/random-pages-from-volpe-pdf-07}\includegraphics[height=2.51cm]{random-pages/random-pages-from-volpe-pdf-08}\includegraphics[height=2.51cm]{random-pages/random-pages-from-volpe-pdf-09} -\par\end{centering} -\begin{centering} -\emph{\vspace{-1.9\baselineskip} -} -\par\end{centering} -\begin{centering} -\emph{Practical functional programming in Scala}, by G.~Volpe (\texttt{\small{}\href{https://leanpub.com/pfp-scala}{https://leanpub.com/pfp-scala}}) -\par\end{centering} -\begin{centering} -\vspace{1\baselineskip} -\par\end{centering} -\begin{centering} -\includegraphics[height=2.51cm]{random-pages/random-pages-from-kalinin-pdf-01}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kalinin-pdf-02}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kalinin-pdf-03}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kalinin-pdf-04}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kalinin-pdf-06}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kalinin-pdf-07}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kalinin-pdf-08}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kalinin-pdf-09} -\par\end{centering} -\vspace{-0.6\baselineskip} - -\begin{centering} -\emph{Mastering advanced Scala}, by D.~Kalinin (\texttt{\small{}\href{https://leanpub.com/mastering-advanced-scala}{https://leanpub.com/mastering-advanced-scala}}) -\par\end{centering} -\caption{Randomly chosen pages from books on Scala programming.\label{fig:Randomly-chosen-pages-1}} -\end{figure} - -\begin{figure} -\begin{centering} -\includegraphics[height=2.51cm]{random-pages/random-pages-from-hefferon-pdf-00}\includegraphics[height=2.51cm]{random-pages/random-pages-from-hefferon-pdf-01}\includegraphics[height=2.51cm]{random-pages/random-pages-from-hefferon-pdf-03}\includegraphics[height=2.51cm]{random-pages/random-pages-from-hefferon-pdf-04}\includegraphics[height=2.51cm]{random-pages/random-pages-from-hefferon-pdf-05}\includegraphics[height=2.51cm]{random-pages/random-pages-from-hefferon-pdf-06}\includegraphics[height=2.51cm]{random-pages/random-pages-from-hefferon-pdf-07} -\par\end{centering} -\vspace{-0.4\baselineskip} - -\begin{centering} -\emph{Linear algebra}, by J.~Hefferon (\texttt{\small{}\href{http://joshua.smcvt.edu/linearalgebra/}{http://joshua.smcvt.edu/linearalgebra/}}) -\par\end{centering} -\begin{centering} -\vspace{0.6\baselineskip} -\par\end{centering} -\begin{centering} -\includegraphics[height=2.51cm]{random-pages/random-pages-from-kibble-pdf-00}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kibble-pdf-01}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kibble-pdf-02}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kibble-pdf-03}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kibble-pdf-04}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kibble-pdf-05}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kibble-pdf-06}\includegraphics[height=2.51cm]{random-pages/random-pages-from-kibble-pdf-09} -\par\end{centering} -\vspace{-0.6\baselineskip} - -\begin{centering} -\emph{Classical mechanics}, by T.~W.~B.~Kibble and F.~H.~Berkshire -(\texttt{\small{}\href{https://archive.org/details/116772449ClassicalMechanics}{https://archive.org/details/116772449ClassicalMechanics}}) -\par\end{centering} -\vspace{0.6\baselineskip} - -\caption{Randomly chosen pages from books on mathematics and physics.\label{fig:Randomly-chosen-pages-2}} -\end{figure} - -\begin{figure} -\begin{centering} -\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpis-pdf-00}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpis-pdf-02}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpis-pdf-04}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpis-pdf-05}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpis-pdf-06}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpis-pdf-07}\includegraphics[height=2.51cm]{random-pages/random-pages-from-fpis-pdf-08} -\par\end{centering} -\vspace{-0\baselineskip} - -\begin{centering} -\emph{Functional programming in Scala}, by P.~Chiusano and R.~Bjarnason -\par\end{centering} -\begin{centering} -\vspace{1\baselineskip} -\par\end{centering} -\begin{centering} -\includegraphics[height=2.51cm]{random-pages/random-pages-from-pdbc-pdf-00}\includegraphics[height=2.51cm]{random-pages/random-pages-from-pdbc-pdf-01}\includegraphics[height=2.51cm]{random-pages/random-pages-from-pdbc-pdf-02}\includegraphics[height=2.51cm]{random-pages/random-pages-from-pdbc-pdf-03}\includegraphics[height=2.51cm]{random-pages/random-pages-from-pdbc-pdf-04}\includegraphics[height=2.51cm]{random-pages/random-pages-from-pdbc-pdf-05}\includegraphics[height=2.51cm]{random-pages/random-pages-from-pdbc-pdf-06}\includegraphics[height=2.51cm]{random-pages/random-pages-from-pdbc-pdf-07} -\par\end{centering} -\vspace{-0.7\baselineskip} - -\begin{centering} -\emph{Program design by calculation}, by J.~N.~Oliveira -\par\end{centering} -\begin{centering} -\vspace{1\baselineskip} -\par\end{centering} -\begin{centering} -\includegraphics[height=2.51cm]{random-pages/random-pages-from-sofp-pdf-00}\includegraphics[height=2.51cm]{random-pages/random-pages-from-sofp-pdf-01}\includegraphics[height=2.51cm]{random-pages/random-pages-from-sofp-pdf-02}\includegraphics[height=2.51cm]{random-pages/random-pages-from-sofp-pdf-05}\includegraphics[height=2.51cm]{random-pages/random-pages-from-sofp-pdf-06}\includegraphics[height=2.51cm]{random-pages/random-pages-from-sofp-pdf-07}\includegraphics[height=2.51cm]{random-pages/random-pages-from-sofp-pdf-08} -\par\end{centering} -\vspace{-0.3\baselineskip} - -\begin{centering} -\emph{The science of functional programming} (this book) -\par\end{centering} -\vspace{1\baselineskip} - -\caption{Randomly chosen pages from books on applied functional type theory.\label{fig:Randomly-chosen-pages}} -\end{figure} - - -\chapter{Essay: Software engineers and software artisans} - -Let us examine what we ordinarily understand by \emph{engineering} -as opposed to artisanship or craftsmanship. It will then become apparent -that today\textsf{'}s computer programmers must be viewed as \textsf{``}software artisans\textsf{''} -rather than software engineers.\footnote{The book reviewed in \texttt{\href{https://www.developerdotstar.com/mag/bookreviews/bitner_craftsmanship.html}{https://www.developerdotstar.com/mag/bookreviews/bitner\_craftsmanship.html}} -proposes a different definition of \textsf{``}software artisans\textsf{''} than this -book, but agrees that software developers work largely as artisans.} - -\section{Engineering disciplines } - -Consider the way mechanical engineers, chemical engineers, or electrical -engineers work, and look at the studies they require for becoming -proficient in their fields. - -A mechanical engineer studies calculus, linear algebra, differential -geometry, and several areas of physics such as theoretical mechanics, -thermodynamics, and elasticity theory,\footnote{\texttt{\href{https://www.colorado.edu/mechanical/academics/undergraduate-program/curriculum}{https://www.colorado.edu/mechanical/academics/undergraduate-program/curriculum}}} -and then uses calculations to guide the design of a bridge. A chemical -engineer studies chemistry, thermodynamics, calculus, linear algebra, -differential equations, some areas of physics such as thermodynamics -and kinetic theory,\footnote{\texttt{\href{https://www.colorado.edu/engineering/sample-undergraduate-curriculum-chemical}{https://www.colorado.edu/engineering/sample-undergraduate-curriculum-chemical}}} -and uses calculations to guide the design of a chemical process. An -electrical engineer studies advanced calculus, linear algebra, and -several areas of physics such as electrodynamics and quantum theory,\footnote{\texttt{\href{http://archive.is/XYLyE}{http://archive.is/XYLyE}}} -and uses calculations to design an antenna or a microchip. - -The common pattern is that engineers use mathematics and natural sciences -in order to create new devices. Mathematical calculations and scientific -reasoning are performed \emph{before} designing or building a real -machine. - -Some of the studies required for engineers include arcane abstract -concepts such as the \textsf{``}Fourier transform of the delta function\textsf{''}\footnote{\texttt{\href{https://www.youtube.com/watch?v=KAbqISZ6SHQ}{https://www.youtube.com/watch?v=KAbqISZ6SHQ}}} -and the \textsf{``}inverse $Z$-transform\textsf{''}\footnote{\texttt{\href{http://archive.is/SsJqP}{http://archive.is/SsJqP}}} -in digital signal processing, \textsf{``}rank 4 tensors\textsf{''}\footnote{\texttt{\href{https://serc.carleton.edu/NAGTWorkshops/mineralogy/mineral_physics/tensors.html}{https://serc.carleton.edu/NAGTWorkshops/mineralogy/mineral\_physics/tensors.html}}} -in calculations of elasticity of materials, \textsf{``}Lagrangians with non-holonomic -constraints\textsf{''}\footnote{\texttt{\href{https://arxiv.org/abs/math/0008147}{https://arxiv.org/abs/math/0008147}}} -in robotics, and the \textsf{``}Gibbs free energy\textsf{''} in chemical reactor design.\footnote{\texttt{\href{https://www.amazon.com/Introduction-Chemical-Engineering-Kinetics-Reactor/dp/1118368258}{https://www.amazon.com/Introduction-Chemical-Engineering-Kinetics-Reactor/dp/1118368258}}} - -To be sure, a significant part of what engineers do is not covered -by any theory: the \emph{know-how}, the informal reasoning, the traditional -knowledge passed on from expert to novice, \textemdash{} all those -skills that are hard to formalize are important. Nevertheless, engineering -is crucially based on natural science and mathematics for some of -its decision-making about new designs. - -\section{Artisanship: Trades and crafts } - -Now consider what kinds of things shoemakers, plumbers, or home painters -do, and what they have to learn in order to become proficient in their -profession. - -A novice shoemaker, for example, begins by copying some drawings\footnote{\texttt{\href{https://youtu.be/cY5MY0czMAk?t=141}{https://youtu.be/cY5MY0czMAk?t=141}}} -and goes on to cutting leather in a home workshop. Apprenticeships -proceed via learning by doing, with comments and instruction from -an expert. After a few years of study (for example, a painter apprenticeship -in California can be as short as 2 years\footnote{\texttt{\href{http://www.calapprenticeship.org/programs/painter_apprenticeship.php}{http://www.calapprenticeship.org/programs/painter\_apprenticeship.php}}}), -a new artisan is ready to start productive work. - -All trades operate entirely from tradition and practical experience. -It takes a prolonged learning effort to become a good artisan in any -profession. But the trades do not require academic study because there -is no formal theory from which to proceed. There are no Fourier transforms -applied to delta functions, no Lagrangians with non-holonomic constraints, -no fourth rank tensors to calculate, nor any differential equations -to solve. - -Artisans do not study science or mathematics because their professions -do not make use of any formal theory for guiding their designs or -processes. - -\section{Programmers today are artisans, not engineers } - -Programmers are \emph{not engineers} in the sense normally used in -the engineering professions. - -\subsection{No requirements of licensing or formal study} - -Mechanical, electrical, chemical engineers are required to pass a -license exam to become certified to work. The license exam certifies -that the person is proficient in applying a known set of engineering -tools and methods. But in software engineering, no certifications -or licenses are required for the job (although many certification -programs exist). - -Working software engineers are also not required to have studied software -engineering or computer science (CS). According to a recent Stack -Overflow survey,\footnote{\texttt{\href{https://thenextweb.com/insider/2016/04/23/dont-need-go-college-anymore-programmer/}{https://thenextweb.com/insider/2016/04/23/dont-need-go-college-anymore-programmer/}}} -about 56\% of working programmers have no CS degree. The author of -this book is a self-taught programmer who has degrees in physics but -never formally studied CS or taken any academic courses in algorithms, -data structures, computer networks, compilers, programming languages, -or other standard CS topics. - -A large fraction of successful programmers have no college degrees -and perhaps \emph{never} studied formally. They acquired all their -knowledge and skills through self-study and practical work. A prominent -example is Robert C.~Martin\index{Robert C.~Martin},\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Robert_C._Martin}{https://en.wikipedia.org/wiki/Robert\_C.\_Martin}}} -an outspoken guru in the arts of programming. He routinely refers -to programmers as artisans\footnote{\texttt{\href{https://blog.cleancoder.com/uncle-bob/2013/02/01/The-Humble-Craftsman.html}{https://blog.cleancoder.com/uncle-bob/2013/02/01/The-Humble-Craftsman.html}}} -and uses the appropriate imagery: novices and masters, trade and craft, -the honor of the guild, etc. He compares programmers to plumbers, -electricians, lawyers, and surgeons, but never to mathematicians, -physicists, or engineers of any kind. According to one of his blog -posts,\footnote{\texttt{\href{https://blog.cleancoder.com/uncle-bob/2013/11/25/Novices-Coda.html}{https://blog.cleancoder.com/uncle-bob/2013/11/25/Novices-Coda.html}}} -he started working at age 17 as a self-taught programmer. He never -went to college and holds no degrees.\footnote{\texttt{\href{https://hashnode.com/post/i-am-robert-c-martin-uncle-bob-ask-me-anything-cjr7pnh8g000k2cs18o5nhulp/2}{https://hashnode.com/post/i-am-robert-c-martin-uncle-bob-ask-me-anything-cjr7pnh8g000k2cs18o5nhulp/2}}} -It is clear that R.~C.~Martin \emph{is} an expert craftsman and -that he did \emph{not} need academic study to master the craft of -programming. - -Here is another opinion\footnote{\texttt{\href{http://archive.is/tAKQ3}{http://archive.is/tAKQ3}}} -(emphasis is theirs): -\begin{quotation} -{\small{}Software Engineering is unique among the STEM careers in -that it absolutely does }\emph{\small{}not}{\small{} require a college -degree to be successful. It most certainly does not require licensing -or certification. }\emph{\small{}It requires experience}{\small{}.}{\small\par} -\end{quotation} -This description fits a career in crafts \textemdash{} but certainly -not a career, say, in electrical engineering. - -The high demand for software developers gave rise to \textsf{``}developer -boot camps\textsf{''}\footnote{\texttt{\href{http://archive.is/GkOL9}{http://archive.is/GkOL9}}} -\textemdash{} vocational schools that educate new programmers in a -few months through purely practical training, with no formal theory -or mathematics involved. These vocational schools are successful\footnote{\texttt{\href{http://archive.is/E9FXP}{http://archive.is/E9FXP}}} -in job placement. But it is unimaginable that a $6$-month crash course -or even a $2$-year vocational school could prepare engineers to work -successfully on designing, say, analog quantum computers\footnote{\texttt{\href{https://www.dwavesys.com/quantum-computing}{https://www.dwavesys.com/quantum-computing}}} -without ever learning quantum physics or calculus. - -\subsection{No mathematical formalism guides software development} - -Most books on software engineering contain no formulas or equations, -no mathematical derivations, and no precise definitions of the various -technical terms they are using (such as \textsf{``}object-oriented\textsf{''} or \textsf{``}module\textsf{'}s -responsibilities\textsf{''}). Some of those books\footnote{E.g., \texttt{\href{https://www.amazon.com/Object-Oriented-Software-Engineering-Unified-Methodology/dp/0073376256}{https://www.amazon.com/Object-Oriented-Software-Engineering-Unified-Methodology/dp/0073376256}}} -also have almost no program code in them. Some of those books are -written by practitioners such as R.\ C.\ Martin never studied any -formalisms and do not think in terms of formalisms. Instead, they -summarize their programming experience in vaguely formulated heuristic -\textquotedblleft principles\textquotedblright .\footnote{\texttt{\href{https://blog.cleancoder.com/uncle-bob/2016/03/19/GivingUpOnTDD.html}{https://blog.cleancoder.com/uncle-bob/2016/03/19/GivingUpOnTDD.html}}} -The programmers are told: \textsf{``}code is about detail\textsf{''}, \textsf{``}never abandon -the big picture\textsf{''}, \textsf{``}avoid tight coupling in your modules\textsf{''}, \textsf{``}a -class must serve a single responsibility\textsf{''}, \textsf{``}strive for good interfaces\textsf{''}, -etc. - -In contrast, textbooks on mechanical or electrical engineering include -a significant amount of mathematics. The design of a microwave antenna -is guided not by an \textsf{``}open and closed module principle\textsf{''} but by -solving the relevant differential equations\footnote{\texttt{\href{https://youtu.be/8KpfVsJ5Jw4?t=447}{https://youtu.be/8KpfVsJ5Jw4?t=447}}} -of electrodynamics. - -Another example of programmers\textsf{'} avoidance of mathematical tools is -given by the \textsf{``}Liskov substitution principle\index{Liskov substitution principle}\textsf{''} -for subtyping\index{subtyping}.\footnote{See \texttt{\href{https://en.wikipedia.org/wiki/Liskov_substitution_principle}{https://en.wikipedia.org/wiki/Liskov\_substitution\_principle}}. -The LSP always holds in functional programming if values are immutable -and subtyping is viewed as an automatic type conversion function (see -Definition~\ref{subsec:Definition-subtyping}). A property $\phi(y)$ -is rewritten as $\phi(c(y))$ by inserting a suitable type conversion -function, $c:S\rightarrow T$. Since $c(y)$ has type $T$ and $\phi(x)$ -holds for all values $x:T$, we find that the property $\phi(c(y))$ -holds automatically.} Its rigorous formulation (\textsf{``}for any property $\phi(x)$ that holds -for all $x$ of type $T$, and for any subtype $S$ of $T$, the property -$\phi(y)$ must also hold for all $y$ of type $S$\textsf{''}) is not used -by programmers. Instead, the literature on object-oriented programming -formulates the principle as \textsf{``}objects of type $T$ may be substituted -by objects of type $S$ while keeping the correctness of the program\textsf{''}. -This formulation \index{object-oriented programming} is both vague -(it does not specify how to choose the substituted objects of type -$S$) and, strictly speaking, incorrect: If the program contains a -function $f(t)$ where $t$ is a value of type $T$, it is not always -possible to find some value $s$ of type $S$ such that $f(s)=f(t)$. -The reason is that some subtyping relations are not surjective, as -shown in Section~\ref{subsec:Subtyping-with-injective} of this book. - -Donald Knuth\textsf{'}s classic textbook \textsf{``}\emph{The Art of Programming}\textsf{''} -indeed treats programming as an art and not as a science. Knuth shows -many algorithms and derives their mathematical properties but gives -almost no examples of realistic program code and does not provide -any theory that could guide programmers in actually \emph{writing} -programs (say, choosing the data types to be used). Knuth assumes -that the reader who understands the mathematical properties of an -algorithm will be able \emph{somehow} to write correct code. - -The books \textsf{``}The Science of Programming\textsf{''}\footnote{\texttt{\href{https://www.amazon.com/Science-Programming-Monographs-Computer/dp/0387964800}{https://www.amazon.com/Science-Programming-Monographs-Computer/dp/0387964800}}} -and \textsf{``}Program derivation\textsf{''}\footnote{\texttt{\href{https://www.amazon.com/Program-Derivation-Development-Specifications-International/dp/0201416247}{https://www.amazon.com/Program-Derivation-Development-Specifications-International/dp/0201416247}}} -are attempts to provide a mathematical basis for writing programs -starting from formal specifications. The books give some methods that -guide programmers in writing code and at the same time produce a proof -that the code conforms to the specification. However, the scope of -proposed methods is limited to designing algorithms for iterative -manipulation of data, such as sorting and searching algorithms. The -procedures suggested in those books are far from a formal mathematical -\emph{derivation} of a wide range of software programs from specifications. -In any case, most programmers today are unaware of these books and -do not use the methods explained there, even when those methods could -apply. - -Today\textsf{'}s computer science courses do not teach a true engineering approach -to software construction. Some courses teach analysis of programs -using mathematical methods. Two such methods are complexity analysis\footnote{\texttt{\href{https://www.cs.cmu.edu/~adamchik/15-121/lectures/Algorithmic\%20Complexity/complexity.html}{https://www.cs.cmu.edu/$\sim$adamchik/15-121/lectures/Algorithmic\%20Complexity/complexity.html}}} -(the \textsf{``}big-$O$ notation\textsf{''}) and formal verification.\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Formal_verification}{https://en.wikipedia.org/wiki/Formal\_verification}}} -But programs are analyzed or verified only \emph{after} they are somehow -written. Theory does not guide the actual \emph{process} of writing -code: it does not define good ways of organizing the code (e.g., how -to decompose the code into modules, classes, or functions) and does -not tell programmers which data structures and type signatures of -functions will be useful to implement. Programmers make these design -decisions on the basis of experience and intuition, trial-and-error, -copy-paste, guesswork, and debugging. - -In a sense, program analysis and verification is analogous to writing -mathematical equations for the surface of a shoe made by a fashion -designer. The resulting \textsf{``}shoe equations\textsf{''} are mathematically rigorous -and can be analyzed or \textsf{``}verified\textsf{''}. But the equations are merely -written after the fact, they do not guide the fashion designers in -actually making shoes. It is understandable that fashion designers -do not study the mathematical theory of surfaces. - -\subsection{Programmers avoid academic terminology } - -Programmers jokingly grumble about terms such as \textsf{``}functor\textsf{''}, \textsf{``}monad\textsf{''}, -or \textsf{``}lambda-functions\textsf{''}: -\begin{quote} -{\small{}Those fancy words used by functional programmers purists -really annoy me. Monads, functors... Nonsense!!! }\footnote{\texttt{\href{http://archive.is/65K3D}{http://archive.is/65K3D}}} -\end{quote} -Perhaps only a small minority of software developers complain about -this, as the majority seems to be unaware of \textsf{``}applicative functors\textsf{''}, -\textsf{``}free monads\textsf{''}, and other arcane terminology. Indeed, that sort -of terminology is intentionally avoided by most books and tutorials -aimed at programmers. - -But why would a software \emph{engineer} wince at \textsf{``}functors\textsf{''} or -at having to verify the laws of a \textsf{``}monad\textsf{''}? Other branches of engineering -use lots of terminology that is far from self-explanatory and requires -some study. Chemical engineers learn about \textsf{``}Gibbs free energy\textsf{''}, -which is a technical term that denotes a certain function. (It does -not mean getting energy from J.~W.~Gibbs\index{Josiah Willard Gibbs} -for free.) Chemical engineers accept the need for studying \textsf{``}phase -diagrams\textsf{''} or \textsf{``}Fourier\textsf{'}s law\textsf{''}. Electrical engineers do not avoid -\textsf{``}Fourier transforms\textsf{''} or \textsf{``}delta functions\textsf{''} because those are -weird things to say. Mechanical engineers take it for granted that -they need \textsf{``}rank 4 tensors\textsf{''}, \textsf{``}Lagrangians\textsf{''}, and \textsf{``}non-holonomic -constraints\textsf{''}. The arcane terminology seems to be the least of their -difficulties, as their textbooks are full of complicated equations -and long derivations. - -Textbooks on true software engineering would have been full of equations -and derivations, teaching engineers how to perform certain calculations -that are required \emph{before} starting to write code. - -\section{Towards true engineering in software} - -It is now clear that we do not presently have true software engineering. -The people employed under that job title are actually artisans. They -work using artisanal methods, and their culture and processes are -that of a crafts guild. - -True software engineering means having a mathematical theory that -guides the process of writing programs, \textemdash{} not just theory -that describes or analyzes programs after they are \emph{somehow} -written. - -It is not enough that the numerical methods required for physics or -the matrix calculations required for data science are \textsf{``}mathematical\textsf{''}. -These programming tasks are indeed formulated using mathematical theory. -However, mathematical \emph{subject matter} (aerospace control, physics -or astronomy simulations, or statistics) does not mean that mathematics -is used to guide the process of writing code. Data scientists, aerospace -engineers, and physicists almost always work as artisans when converting -their computations into program code. - -We expect that software engineers\textsf{'} textbooks should be full -of equations and derivations. What theory would those equations represent? - -This theory is what this book calls \textbf{applied functional type -theory}\index{applied functional type theory} (see Chapter~\ref{chap:Applied-functional-type}). -It represents the mathematical foundation of the modern practice of -functional programming, as implemented in languages such as OCaml, -Haskell, and Scala. This theory is a blend of set theory, category -theory, and logical proof theory, adapted for the needs of programmers. -It has been in development since late 1990s and is still being actively -worked on by a community of software practitioners and academic computer -scientists. - -To appreciate that functional programming, unlike any other programming -paradigm, is based on a \emph{theory that guides coding}, we can look -at some recent software engineering conferences such as \textsf{``}Scala By -the Bay\textsf{''}\footnote{\texttt{\href{http://2015.scala.bythebay.io/}{http://2015.scala.bythebay.io/}}} -or BayHac,\footnote{\texttt{\href{http://bayhac.org/}{http://bayhac.org/}}} -or at the numerous FP-related online tutorials and blogs. We cannot -fail to notice that speakers devote significant time to a peculiar -kind of applied mathematical reasoning. Rather than focusing on one -or another API or algorithm, as it is often the case with other software -engineering blogs or presentations, an FP speaker describes a \emph{mathematical -structure} \textemdash{} such as the \textsf{``}applicative functor\textsf{''}\footnote{\texttt{\href{http://www.youtube.com/watch?v=bmIxIslimVY}{http://www.youtube.com/watch?v=bmIxIslimVY}}} -or the \textsf{``}free monad\textsf{''}\footnote{\texttt{\href{http://www.youtube.com/watch?v=U0lK0hnbc4U}{http://www.youtube.com/watch?v=U0lK0hnbc4U}}} -\textemdash{} and illustrates its use for practical coding. - -These people are not graduate students showing off their theoretical -research. They are practitioners, software engineers who use FP on -their jobs. It is just the nature of FP that certain mathematical -tools and constructions are directly applicable to practical programming -tasks. - -These mathematical tools are not mere tricks for a specific programming -language; they apply equally to all FP languages. Before starting -to write code, the programmer can jot down certain calculations in -a mathematical notation (see Fig.\ \ref{fig:Example-calculation-in-type-theory}). -The results of those calculations will help design the code fragment -the programmer is about to write. This activity is similar to that -of an engineer who performs some mathematical calculations before -embarking on a design project. \begin{wrapfigure}{L}{0.5\textwidth}% -\begin{centering} -{\footnotesize{}\vspace{0.25\baselineskip} -\includegraphics[width=1\linewidth]{ftt-example}\vspace{-0.25\baselineskip} -}{\footnotesize\par} -\par\end{centering} -{\footnotesize{}\caption{A programmer performs a derivation before writing Haskell code.\label{fig:Example-calculation-in-type-theory}} -}{\footnotesize\par} - -\vspace{-0.5\baselineskip} -\end{wrapfigure}% - - -\noindent A recent example of a development in applied functional -type theory is the \textsf{``}free applicative functor\textsf{''} construction. It -was first described in a 2014 paper;\footnote{\texttt{\href{https://arxiv.org/pdf/1403.0749.pdf}{https://arxiv.org/pdf/1403.0749.pdf}}} -a couple of years later, a combined free applicative / free monad -data type was designed and its implementation proposed in Scala\footnote{\texttt{\href{https://github.com/typelevel/cats/issues/983}{https://github.com/typelevel/cats/issues/983}}} -as well as in Haskell.\footnote{\texttt{\href{https://elvishjerricco.github.io/2016/04/08/applicative-effects-in-free-monads.html}{https://elvishjerricco.github.io/2016/04/08/applicative-effects-in-free-monads.html}}} -This technique allows programmers to implement declarative side-effect -computations where some parts are sequential but other parts are computed -in parallel, and to achieve the parallelism \emph{automatically} while -maintaining the composability of the resulting programs. The new technique -has advantages over monad transformers, which was a previously known -method of composing declarative side-effects. The combined \textsf{``}free -applicative / free monad\textsf{''} code was designed and implemented by true -software engineers. They first derived the type constructor that has -the necessary algebraic properties. Guided by the resulting type formula, -they wrote code that was guaranteed to work as intended. - -Another example of using applied functional type theory is the \textsf{``}\index{tagless final}tagless -final\textsf{''} encoding of effects, first described\footnote{\texttt{\href{http://okmij.org/ftp/tagless-final/index.html}{http://okmij.org/ftp/tagless-final/index.html}}} -in 2009. That technique (called \textsf{``}Church-encoded free monad\index{free monad}\textsf{''} -in the present book) has advantages over the ordinary free monad in -a number of cases \textemdash{} just as the free monad itself was -used to cure certain problems with monad transformers.\footnote{\texttt{\href{http://blog.ezyang.com/2013/09/if-youre-using-lift-youre-doing-it-wrong-probably/}{http://blog.ezyang.com/2013/09/if-youre-using-lift-youre-doing-it-wrong-probably/}}} -The new encoding is not tied to a specific programming language. Rather, -it is a language-agnostic construction that was originally described -in OCaml and later used in Haskell and Scala, but can be made to work -even in Java,\footnote{\texttt{\href{https://oleksandrmanzyuk.wordpress.com/2014/06/18/}{https://oleksandrmanzyuk.wordpress.com/2014/06/18/}}} -which is not an FP language. - -This example shows that we may need several more years of work before -the practical aspects of using applied functional type theory are -sufficiently well understood by the FP community. The theory is in -active development, and its design patterns \textemdash{} as well -as the exact scope of the requisite theoretical material \textemdash{} -are still being figured out. If the 40-year gap hypothesis\footnote{\texttt{\href{https://www.linkedin.com/pulse/40-year-gap-what-has-academic-computer-science-ever-done-winitzki}{https://www.linkedin.com/pulse/40-year-gap-what-has-academic-computer-science-ever-done-winitzki}}} -holds, we should expect applied functional type theory (perhaps under -a different name) to become mainstream by 2030. This book is a step -towards a clear designation of the scope of that theory. - -\section{Does software need engineers, or are artisans sufficient? } - -The demand for programmers is growing. \textsf{``}Software developer\textsf{''} was -\#1 best job\footnote{\texttt{\href{http://money.usnews.com/money/careers/articles/how-us-news-ranks-the-best-jobs}{http://money.usnews.com/money/careers/articles/how-us-news-ranks-the-best-jobs}}} -in the US in 2018. But is there a demand for engineers or just for -artisans? - -We do not seem to be able to train enough software artisans.\footnote{\texttt{\href{http://archive.is/137b8}{http://archive.is/137b8}}} -So, it is probably impossible to train as many software engineers -in the true sense of the word. Modern computer science courses do -not actually train engineers in that sense. Instead, they train academic -researchers who can also work as software artisans and write code. - -Looking at the situation in construction business in the U.S.A., we -find that it employs about $10$ times more construction workers as -architects. We might conclude that perhaps one software engineer is -required per dozen software artisans. - -What is the price of \emph{not} having engineers, of replacing them -with artisans? - -Software practitioners have long bemoaned the permanent state of \textsf{``}crisis\textsf{''} -in software development. Code \textsf{``}rots with time\textsf{''}, its complexity -grows \textsf{``}out of control\textsf{''}, and operating systems have been notorious -for ever-appearing security flaws\footnote{\texttt{\href{http://archive.fo/HtQzw}{http://archive.fo/HtQzw}}} -despite many thousands of programmers and testers employed. It appears -that the growing complexity of software tends to overwhelm the capacity -of the human brain for correct \emph{artisanal} programming. - -It is precisely in designing large and robust software systems that -we would benefit from true engineering. Artisans has been building -bridges and using chemical reactions by trial and error and via tradition, -long before mechanical or chemical engineering disciplines were developed -and founded upon rigorous theory. But once the theory became available, -engineers were able to design unimaginably more powerful and complicated -structures, devices, and processes. So, we may expect that trial, -error, and adherence to tradition is inadequate for some of the more -complex software development tasks in front of us. - -To build large and reliable software, such as new mobile or embedded -operating systems or distributed peer-to-peer trust architectures, -we will most likely need the qualitative increase in productivity -and reliability that can only come from replacing artisanal programming -by a true engineering discipline. Applied functional type theory and -functional programming are steps in that direction. - -\chapter{Essay: Towards functional data engineering with Scala} - -Data engineering is among the highest-demand\footnote{\texttt{\href{http://archive.is/mK59h}{http://archive.is/mK59h}}} -novel occupations in the IT world today. Data engineers create software -pipelines that process large volumes of data efficiently. Why did -the Scala programming language emerge as a premier tool\footnote{\texttt{\href{https://www.slideshare.net/noootsab/scala-the-unpredicted-lingua-franca-for-data-science}{https://www.slideshare.net/noootsab/scala-the-unpredicted-lingua-franca-for-data-science}}} -for crafting the foundational data engineering technologies such as -Spark or Akka? Why is Scala in high demand\footnote{\texttt{\href{https://techcrunch.com/2016/06/14/scala-is-the-new-golden-child/}{https://techcrunch.com/2016/06/14/scala-is-the-new-golden-child/}}} -within the world of big data? - -There are reasons to believe that the choice of Scala was not accidental. - -\section{Data is math} - -Humanity has been working with data at least since Babylonian tax -tables\footnote{\texttt{\href{https://www.nytimes.com/2017/08/29/science/trigonometry-babylonian-tablet.html}{https://www.nytimes.com/2017/08/29/science/trigonometry-babylonian-tablet.html}}} -and the ancient Chinese number books.\footnote{\texttt{\href{https://web.archive.org/web/20170425233550/https://quatr.us/china/science/chinamath.htm}{https://quatr.us/china/science/chinamath.htm}}} -Mathematics summarizes several millennia\textsf{'}s worth of data processing -experience in a few fundamental tenets: - -\begin{wrapfigure}{I}{0.34\columnwidth}% -\begin{centering} -\vspace{-0.65\baselineskip} -\includegraphics[width=0.96\linewidth]{type-error}\vspace{-0.5\baselineskip} -\par\end{centering} -\caption{\index{jokes}Mixing incompatible data types produces nonsensical -results.\label{fig:A-nonsensical-calculation}} - -\vspace{-3.5\baselineskip} -\end{wrapfigure}% - -\begin{itemize} -\item Data is \emph{immutable}, because facts are immutable. -\item Each \emph{type} of values (population count, land area, distance, -price, location, time, etc.) needs to be handled separately; it is -meaningless to add a distance to a population count. -\item Data processing should be performed according to \emph{mathematical -formulas}. -\end{itemize} -Violating these tenets produces nonsense (see Fig.\ \ref{fig:A-nonsensical-calculation} -for a real-life illustration). - -The power of the principles of mathematics extends over all epochs -and all cultures; math is the same in San Francisco, in Rio de Janeiro, -in Kuala-Lumpur, and in Pyongyang (Fig.\ \ref{fig:The-Pyongyang-method-of-error-free-programming}). - -\section{Functional programming is math} - -The functional programming paradigm is based on mathematical principles: -values are immutable, data processing is coded through formula-like -expressions, and each type of data is required to match correctly -during the computations. The type-checking process automatically prevents -programmers from making many kinds of coding errors. In addition, -programming languages such as Scala and Haskell have a set of features -adapted to building powerful abstractions and domain-specific languages. -This power of abstraction is not accidental. Since mathematics is -the ultimate art of building abstractions, math-based functional programming -languages capitalize on having several millennia of mathematical experience. - -A prominent example of how mathematics informs the design of programming -languages is the connection between constructive logic\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Intuitionistic_logic}{https://en.wikipedia.org/wiki/Intuitionistic\_logic}}} -and the programming language\textsf{'}s type system, called the Curry-Howard -(CH) correspondence. The main idea of the CH correspondence\index{Curry-Howard correspondence} -is to think of programs as mathematical formulas that compute a value -of a certain type $A$. The CH correspondence is between programs -and logical propositions: To any program that computes a value of -type $A$, there corresponds a proposition stating that \textsf{``}a value -of type $A$ can be computed\textsf{''}. - -This may sound rather theoretical so far. To see the real value of -the CH correspondence, recall that formal logic has operations \textsf{``}\textbf{\emph{and}}\textsf{''}, -\textsf{``}\textbf{\emph{or}}\textsf{''}, and \textsf{``}\textbf{\emph{implies}}\textsf{''}. For any -two propositions $A$, $B$, we can construct the propositions \textsf{``}$A$ -\textbf{\emph{and}} $B$\textsf{''}, \textsf{``}$A$ \textbf{\emph{or}} $B$\textsf{''}, \textsf{``}$A$ -\textbf{\emph{implies}} $B$\textsf{''}. These three logical operations are -foundational; without one of them, the logic is \emph{incomplete} -(you cannot derive some theorems). - -A programming language \textbf{obeys the CH correspondence}\index{Curry-Howard correspondence} -with the logic if for any types $A$, $B$, the language also contains -composite types corresponding to the logical formulas \textsf{``}$A$ \textbf{\emph{or}} -$B$\textsf{''}, \textsf{``}$A$ \textbf{\emph{and}} $B$\textsf{''}, \textsf{``}$A$ \textbf{\emph{implies}} -$B$\textsf{''}. In Scala, these composite types are \lstinline!Either[A, B]!, -the tuple \lstinline!(A,B)!, and the function type \lstinline!A => B!. -All modern functional languages such as OCaml, Haskell, Scala, F\#, -Swift, Elm, and PureScript support these three type constructions -and thus obey the CH correspondence. Having a \emph{complete} logic -in a language\textsf{'}s type system enables declarative domain-driven code -design.\footnote{\texttt{\href{https://fsharpforfunandprofit.com/ddd/}{https://fsharpforfunandprofit.com/ddd/}}} - -\begin{wrapfigure}{I}{0.5\columnwidth}% -\begin{centering} -\vspace{-0.5\baselineskip} -\includegraphics[width=1\linewidth]{no-bugs}\vspace{-0.5\baselineskip} -\par\end{centering} -\caption{\index{jokes}The Pyongyang method of error-free software engineering.\label{fig:The-Pyongyang-method-of-error-free-programming}} -\vspace{-3\baselineskip} -\end{wrapfigure}% - -It is interesting to note that most older programming languages (C/C++, -Java, JavaScript, Python) do not support some of these composite types. -In other words, these programming languages have type systems based -on an incomplete logic. As a result, users of these languages have -to implement burdensome workarounds that make for error-prone code. -Failure to follow mathematical principles has real costs (Figure~\ref{fig:The-Pyongyang-method-of-error-free-programming}). - -\section{The power of abstraction} - -Early adopters of Scala, such as Netflix, LinkedIn, and Twitter, were -implementing what is now called \textsf{``}big data engineering\textsf{''}. The required -software needs to be highly concurrent, distributed, and resilient -to failure. Those software companies used Scala as their main implementation -language and reaped the benefits of functional programming. - -What makes Scala suitable for big data tasks? The only reliable way -of managing massively concurrent code is to use sufficiently high-level -abstractions that make application code declarative. The two most -important such abstractions are the \textsf{``}resilient distributed dataset\textsf{''} -(RDD) of Apache Spark and the \textsf{``}reactive stream\textsf{''} used in systems -such as Kafka, Akka Streams, and Apache Flink. While these abstractions -are certainly implementable in Java or Python, a fully declarative -and type-safe usage is possible only in a programming language with -a sophisticated type system. Among the currently available mature -functional languages, only Scala and Haskell are technically adequate -for that task, due to their support for typeclasses and higher-order -types. The early adopters of Scala were able to benefit from the powerful -abstractions Scala supports. In this way, Scala enabled those businesses -to engineer and to scale up their massively concurrent computations. - -It remains to see why Scala (and not, say, OCaml or Haskell) became -the \emph{lingua franca} of big data. - -\section{Scala is Java on math } - -The recently invented general-purpose functional programming languages -may be divided into \textsf{``}academic\textsf{''} (OCaml, Haskell) and \textsf{``}industrial\textsf{''} -(F\#, Scala, Swift). - -The \textsf{``}academic\textsf{''} languages are clean-room implementations of well-researched -mathematical principles of programming language design (the CH correspondence -being one such principle). These languages are not limited by requirements -of compatibility with any existing platforms or libraries. Because -of this, the \textsf{``}academic\textsf{''} languages have been designed and used -for pursuing various mathematical ideas to their logical conclusion.\footnote{OCaml has arbitrary recursive product and co-product types that can -be freely combined with object-oriented types. Haskell removes all -side effects from the language and supports partial type functions -of arbitrarily high order.} At the same time, software practitioners struggle to adopt these -programming languages due to a steep learning curve, a lack of enterprise-grade -libraries and tool support, and immature package management. - -The languages from the \textsf{``}industrial\textsf{''} group are based on existing -and mature software ecosystems: F\# on .NET, Scala on JVM, and Swift -on the MacOS/iOS platform. One of the important design requirements -for these languages is 100\% binary compatibility with their \textsf{``}parent\textsf{''} -platforms and languages (F\# with C\#, Scala with Java, and Swift -with Objective-C). Because of this, developers can immediately take -advantage of the existing tooling, package management, and industry-strength -libraries, while slowly ramping up the idiomatic usage of new language -features. However, the same compatibility requirements dictate certain -limitations in the languages, making their design less than fully -satisfactory from the functional programming viewpoint. - -It is now easy to see why the adoption rate of the \textsf{``}industrial\textsf{''} -group of languages is much higher\footnote{\texttt{\href{https://www.tiobe.com/tiobe-index/}{https://www.tiobe.com/tiobe-index/}}, -archived in 2019 at \texttt{\href{http://archive.is/RsNH8}{http://archive.is/RsNH8}}} than that of the \textsf{``}academic\textsf{''} languages. The transition to the -functional paradigm is also smoother for software developers because -F\#, Scala, and Swift seamlessly support the familiar object-oriented -programming\index{object-oriented programming} paradigm. At the same -time, these new languages still have logically complete type systems, -which gives developers an important benefit of type-safe domain modeling. - -Nevertheless, the type systems of these languages are not equally -powerful. For instance, F\# and Swift are similar to OCaml in many -ways but omit OCaml\textsf{'}s parameterized modules and some other features. -Of all mentioned languages, only Scala and Haskell directly support -typeclasses and higher-order functions on types, which are helpful -for expressing abstractions such as automatically parallelized data -sets or asynchronous data streams. - -To see the impact of these advanced features, consider LINQ, a domain-specific -language for database queries on .NET, implemented in C\# and F\# -through a special built-in syntax supported by Microsoft\textsf{'}s compilers. -Analogous functionality is provided in Scala as a \emph{library}, -without need to modify the Scala compiler, by several open-source -projects such as Slick and Quill. Similar libraries exist for Haskell -\textemdash{} but not in languages with less powerful type systems. - -\section{Summary} - -Only Scala has all of the features required for industrial-grade functional -programming: -\begin{enumerate} -\item Functional collections in the standard library. -\item A sophisticated type system with support for typeclasses and higher-order -types. -\item Seamless compatibility with a mature software ecosystem (JVM). -\end{enumerate} -Based on this assessment, we may be confident in Scala\textsf{'}s future as -a main implementation language for big data engineering. - -\chapter{Essay: Why category theory is useful in functional programming} - -\index{category theory!in functional programming}This essay is for -readers who are already somewhat familiar with category theory. - -\section{A \textquotedblleft types/functions\textquotedblright{} category for -a programming language} - -We consider programming languages that support various data types, -such as integers (\lstinline!Int!), floating-point numbers (\lstinline!Float!), -strings (\lstinline!String!), arrays of strings (\lstinline!Array[String]!), -and so on. Such languages allow programmers to define functions with -specified types of arguments and return values. The compiler will -then verify that all functions are always applied to arguments of -correct types, and also that all return values have the expected types. - -To each programming language of that kind, there corresponds a \textsf{``}types/functions\textsf{''} -category: -\begin{itemize} -\item The objects of the category are all the data types supported by the -language (including user-defined data types). As an example, for Scala -there will be an object \lstinline!Int!, an object \lstinline!Float!, -an object \lstinline!String!, an object \lstinline!Array[String]!, -and so on. -\item The morphisms between objects \lstinline!A! and \lstinline!B! are -all functions implementable in the language (with finitely long program -code) that take a single argument of type \lstinline!A! and return -a value of type \lstinline!B!. -\item We assume that the computer has countably infinite memory, so objects -can be viewed as (at most) countably infinite sets. Morphisms will -also form at most countably infinite sets. -\end{itemize} -The category defined in this way will typically have a large number -of morphisms between most objects. For example, morphisms between -objects \lstinline!Boolean! and \lstinline!Int! are functions that -take a single argument of type \lstinline!Boolean! and return a value -of type \lstinline!Int!. There are as many such functions as pairs -of integers. Scala code for one of those morphisms looks like this: -\begin{lstlisting} -def morphismBooleanToInt: Boolean => Int = { b => if (b) 123 else 456 } -\end{lstlisting} - -Why do the category laws hold? The composition of morphisms corresponds -to composition of functions, which we can implement by writing code -that applies the first function and then applies the second function. -In Scala: -\begin{lstlisting} -def composeMorphisms[A, B, C](f: A => B, g: B => C): A => C = { a => g(f(a)) } -\end{lstlisting} -Equivalent functionality can be implemented in most programming languages. - -The category\textsf{'}s identity law says that there must be a morphism between -objects \lstinline!A! and \lstinline!A!. This can be implemented -in most programming languages as a function that returns its argument -unchanged: -\begin{lstlisting} -def identity[A]: A => A = { x => x } -\end{lstlisting} -One can check that morphism composition is associative and agrees -with the identity morphism. - -For a given programming language, we have thus defined the \textsf{``}types/functions -category\textsf{''}, which can be seen as a subcategory of the category of -sets. Most of the time, we will be working with that category, or -with the category of its endofunctors, or with a sub-category of these -categories. - -Different programming languages will give rise to different \textsf{``}types/functions\textsf{''} -categories, but all those categories have many common features that -are especially important in languages designed for functional programming -(the \textsf{``}FP languages\textsf{''}, such as OCaml, Haskell, Scala and others). - -\section{The use of endofunctors} - -An endofunctor in the \textsf{``}types/functions\textsf{''} category is a mapping -of types together with a mapping of functions. A good example is the -\lstinline!Array! data type. In some programming languages, the type -of an array\textsf{'}s elements can be specified and enforced throughout the -code. For example, in Scala one can use the type \lstinline!Array[Int]! -for an array of integers, \lstinline!Array[String]! for an array -of strings, \lstinline!Array[Array[Int]]! for an array containing -nested arrays of integers, etc. So, \lstinline!Array! can be seen -as a mapping from types to types: it maps the type \lstinline!Int! -to the type \lstinline!Array[Int]!, the type \lstinline!String! -to the type \lstinline!Array[String]!, etc. For any type \lstinline!X!, -we have the type \lstinline!Array[X]!. This is the object-to-object -map of an endofunctor. - -An endofunctor also needs a map from morphisms to morphisms. Given -a function \lstinline!f: X => Y!, we need to implement a function -of type \lstinline!Array[X] => Array[Y]!. This can be done by writing -a loop over the array and applying the function \lstinline!f! to -each element (of type \lstinline!X!). The resulting values (of type -\lstinline!Y!) are then collected in a new array, of type \lstinline!Array[Y]!. - -This code can be written in many programming languages in a generic -manner, using type parameters such as \lstinline!X! and \lstinline!Y!. -The same code will then work for arrays and functions of any given -type. In Scala, the code could be written as the following function -(usually called \lstinline!fmap! in FP libraries): -\begin{lstlisting} -def fmap[X, Y: ClassTag](f: X => Y): Array[X] => Array[Y] = { arrayX: Array[X] => - val arrayY = new Array[Y](arrayX.size) - for { i <- arrayX.indices } arrayY(i) = f(arrayX(i)) - arrayY // Return this array of type Array[Y]. -} -\end{lstlisting} -One can then check that the code of \lstinline!fmap! satisfies the -identity and composition laws of endofunctors. This completes the -implementation of the \lstinline!Array! endofunctor. - -Why does \lstinline!fmap! satisfy the laws of endofunctors? The categorical -properties of functions are preserved if we apply functions to each -element of an array and collect the results \emph{in the same order}. -An identity function applied to every element will not modify the -array. Function composition is preserved because a composition of -two functions will be applied separately to each array element. - -The same construction can be applied to many data structures other -than arrays. It turns out that many programs can be reformulated using -the operation of applying a function to every value in a data structure -(i.e., the function \lstinline!fmap!). This reformulation leads to -code that avoids loops: the loops are replaced by \lstinline!fmap! -functions of some endofunctors, and all those \lstinline!fmap! functions -are implemented in a standard library. In practice, code written via -\lstinline!fmap! instead of loops is more concise and admits fewer -opportunities for errors. The programmer\textsf{'}s intuition about \textsf{``}applying -functions to every value held within a data structure\textsf{''} is then directly -represented by the formal laws of endofunctors. Once those laws are -verified, the programmer is assured that the code written via \lstinline!fmap! -will work according to the programmer\textsf{'}s intuitive expectations. - -\section{The use of natural transformations} - -What is a natural transformation between endofunctors in the \textsf{``}types/functions\textsf{''} -category? For two given endofunctors \lstinline!F! and \lstinline!G!, -a natural transformation \lstinline!t: F ~> G! is defined by its -components. The component at object \lstinline!X! is a function of -type \lstinline!F[X] => G[X]!; this must be defined for all \lstinline!X!. -Some programming languages support functions with type parameters. -In Scala, the syntax is -\begin{lstlisting} -def t[X]: F[X] => G[X] = ... -\end{lstlisting} -The code of such a function is written once and will work in the same -way for all types \lstinline!X!. - -An example of natural transformation is a function that reverses the -order of elements in an array: -\begin{lstlisting} -def reverse[X]: Array[X] => Array[X] = ... -\end{lstlisting} -The algorithm is \textsf{``}fully parametric\textsf{''}: it is written in the same -way for all type parameters \lstinline!X!. - -It turns out that, by the Reynolds-Wadler parametricity theorem, any -code written in a fully parametric manner will satisfy the law of -a natural transformation (the naturality law). The naturality law -states that applying the endofunctor \lstinline!F!\textsf{'}s morphism map -before a natural transformation \lstinline!t! must be equal to applying -the endofunctor \lstinline!G!\textsf{'}s map after \lstinline!t!. In Scala -syntax, the law is written as -\begin{lstlisting} -t(fmap_F(f)(x)) == fmap_G(f)(t(x)) -\end{lstlisting} -This law can be verified directly for a given code of \lstinline!t! -and with known code for \lstinline!fmap_F! and \lstinline!fmap_G!. - -Naturality laws are satisfied by transformations that rearrange data -items in a data structure in some way that does not depend on specific -values or types. In this way, the formal laws of natural transformations -directly represent programmers\textsf{'} intuitions about code that works \textsf{``}in -the same way for all type parameters\textsf{''}. - -As we have just seen, the notions of endofunctors and natural transformations -are useful in programming languages that support types with type parameters -(such as \lstinline!Array[X]!) and functions with type parameters -(such as \lstinline!reverse[X]!). Programming languages that do not -support those features cannot benefit from the powerful reasoning -tools of category theory. - -\section{Other properties of the \textquotedblleft types/functions\textquotedblright{} -category} - -Morphisms in the \textsf{``}types/functions\textsf{''} category are always functions -of a single argument. However, programming languages usually support -functions with many arguments. There are two ways to imitate such -functions: tupling and currying. - -Tupling means that we put all arguments into a compound structure -(a pair, a triple, etc.). The function is still viewed as having a -single argument, but the type of that argument is the type of a pair, -or a triple, or a longer tuple type. This works when the programming -language supports tuple types. A tupled function\textsf{'}s type is written -(in Scala) as, e.g., \lstinline!((A, B, C)) => D!. - -Tuple types correspond to finite products of objects in the \textsf{``}types/functions\textsf{''} -category. So, it is useful if the category has (finite) products. - -Currying means that we create a function that takes the first argument -and returns a curried function that handles the rest of the arguments -in the same way (takes the second argument and again returns a function, -etc.). A curried function\textsf{'}s type is written in Scala as, e.g., \lstinline!A => B => C => D!. -To support this method, the programming language should have function -types. The corresponding categorical construction is the \textsf{``}exponential\textsf{''} -object. - -In the practice of functional programming, it has been found useful -to have the type \lstinline!Unit!, which has exactly one value, and -the type \lstinline!Nothing!, which has no values. In the \textsf{``}types/functions\textsf{''} -category, these types correspond to the terminal and the initial objects. - -Finally, disjunctive types correspond to co-products in the \textsf{``}types/functions\textsf{''} -category. - -In this way, we find that various well-known mathematical properties -of the \textsf{``}types/functions\textsf{''} category (initial and terminal objects, -finite products and co-products, exponentials) correspond to properties -of the programming language that proved useful in the practice of -software engineering. - -\section{Some useful sub-categories of endofunctors} - -Besides loops that apply functions to array elements, other frequently -used computations are nested loops and \textsf{``}while\textsf{''}-loops that are -repeated while a given condition holds and then stopped. It turns -out that category theory provides a convenient language for reasoning -about such computations. Similarly to representing loops via endofunctors, -the various kinds of loops are encoded via certain sub-categories -of endofunctors in the \textsf{``}types/functions\textsf{''} category. - -To see how this works, we need to define an auxiliary sub-category -called the \textsf{``}\lstinline!F!-lifted\textsf{''} (where \lstinline!F! may be -any given endofunctor, such as \lstinline!Array!). The \lstinline!F!-lifted -sub-category is the image of the endofunctor \lstinline!F!. The objects -of that sub-category are types of the form \lstinline!F[A]!. The -morphisms of that sub-category are functions of type \lstinline!F[A] => F[B]! -(not necessarily obtained by lifting some function \lstinline!f: A => B! -through the endofunctor \lstinline!F!\textsf{'}s \lstinline!fmap! method). - -\subsection{Filterable endofunctors} - -To describe \textsf{``}while\textsf{''}-loops using category theory, we begin by reformulating -the loop as a \emph{mathematical function} rather than as a sequence -of computer instructions. To be specific, consider a program with -a loop that stops when a certain condition first becomes \lstinline!false!. -A loop of this kind may be modeled by a function that takes an initial -array as argument and returns a new array that is truncated when a -given condition first becomes \lstinline!false!. The condition is -represented by a function evaluated on each element of the array. -The Scala standard library includes such a function (\lstinline!takeWhile!). -An example of its usage is: -\begin{lstlisting}[mathescape=true] -scala> Array(1, 2, 3, 4, 5).takeWhile(x => x < 4) // Truncate the array when $\color{dkgreen} x \geq 4$ is first found. -res0: Array[Int] = Array(1, 2, 3) -\end{lstlisting} -The next step is to extend this function to work with arbitrary types -instead of integers. The type signature of \lstinline!takeWhile! -may be written as: -\begin{lstlisting} -def takeWhile[X](p: X => Boolean): Array[X] => Array[X] -\end{lstlisting} -Here \lstinline!X! is a type parameter. The first argument (\lstinline!p!) -is a predicate of type \lstinline!X => Boolean!. - -Finally, we write the laws that we expect this function to satisfy. -For instance, if the given predicate \lstinline!p! always returns -\lstinline!true!, the function should not change the given array -(the \textsf{``}identity law\textsf{''}): -\begin{lstlisting} -takeWhile(x => true) == identity -\end{lstlisting} -Another plausible law is called the \textsf{``}composition law\textsf{''}. If we apply -\lstinline!takeWhile! with a predicate \lstinline!p1! and then again -apply \lstinline!takeWhile! with another predicate \lstinline!p2!, -the resulting truncated array should be the same as if we applied -\lstinline!takeWhile! just once with a Boolean conjunction of \lstinline!p1! -and \lstinline!p2!: -\begin{lstlisting} -takeWhile(p1) andThen takeWhile(p2) == takeWhile( x => p1(x) && p2(x) ) -\end{lstlisting} - -The identity and composition laws of \lstinline!takeWhile! are analogous -to the identity and composition laws of functors. More precisely, -one can derive the laws of \lstinline!takeWhile! from the laws of -an auxiliary functor between a certain Kleisli category and the \lstinline!F!-lifted -category (see Example~\ref{subsec:Example-category-definition-of-filterable-functor} -for details). That auxiliary functor exists only for some endofunctors -\lstinline!F!, which are called \textsf{``}filterable\textsf{''} in this book. Filterable -endofunctors are a sub-category of all endofunctors of the \textsf{``}types/functions\textsf{''} -category. - -With this construction, one may now regard the laws of \lstinline!takeWhile! -not as arbitrarily postulated properties but as a consequence of the -functor laws. In this way, category theory validates the programmer\textsf{'}s -intuition for the choice of the laws for \lstinline!takeWhile!. - -\subsection{Monadic endofunctors} - -To evaluate a nested loop, which may be written in Scala as in this -example: -\begin{lstlisting} -for { - x <- 1 to 10 - y <- 1 to x / 2 -} yield f(x, y) // Some computation that may use x and y. -\end{lstlisting} -the computer will perform ten repetitions of the inner loop over \lstinline!y!. -This computation is equivalent to converting the nested loop into -an ordinary, \textsf{``}flattened\textsf{''} loop that has a larger total number of -repetitions (in this example, $25$ repetitions). To describe this -situation using category theory, we start by reformulating a nested -loop into a mathematical function. The arguments of that function -are the first array (\lstinline!1 to 10!) for iterating with \lstinline!x!, -and a function from a value of \lstinline!x! to the nested array -(\lstinline!x => 1 to x / 2!). The function returns a \textsf{``}flattened\textsf{''} -array of $25$ values. - -The Scala library contains such a function, named \lstinline!flatMap!. -An example of usage is: -\begin{lstlisting} -scala> (1 to 10).toArray.flatMap(x => 1 to x / 2) -res0: Array[Int] = Array(1, 1, 1, 2, 1, 2, 1, 2, 3, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5) -\end{lstlisting} -This function can be used repeatedly to convert arbitrarily deeply -nested loops into \textsf{``}flat\textsf{''} loops. - -The next step is to formulate a fully parametric type signature for -\lstinline!flatMap!: -\begin{lstlisting} -def flatMap[A, B](f: A => Array[B]): Array[A] => Array[B] -\end{lstlisting} -In this way, \lstinline!flatMap! can transform arrays with elements -of any type. - -The \lstinline!flatMap! function must satisfy certain properties -that are useful for practical programming. One of these properties -is \textsf{``}associativity\textsf{''}. A deeply nested loop may be flattened by applying -\lstinline!flatMap! first to the outer layers and then to the inner -layers, or by applying \lstinline!flatMap! first to the inner layers; -the results must be the same. This and other properties of \lstinline!flatMap! -are analogous to the laws of a category: there are two identity laws -and one associativity law. More precisely, one can derive the laws -of \lstinline!flatMap! from the requirement that the Kleisli category -on the endofunctor \lstinline!Array! is well-defined (see Section~\ref{subsec:Monads-in-category-theory-monad-morphisms} -for details). This is equivalent to saying that \lstinline!Array! -is a monad. Monads form a sub-category of endofunctors of the \textsf{``}types/functions\textsf{''} -category. - -\section{Category theory and the laws of FP idioms} - -We have seen that \textsf{``}while\textsf{''}-loops and nested loops can be reformulated -through type-parameterized functions satisfying certain laws. Those -laws are then equivalent to the laws of suitably chosen functors or -categories. This turns out to be a general pattern: -\begin{itemize} -\item Begin with a known idiom of computation (e.g., a certain kind of a -loop). -\item Reformulate that idiom through functions with parameterized argument -types. -\item Write the laws that programmers expect those functions to satisfy. -\item Prove that those laws are equivalent to the laws of a suitable functor -and/or category. -\end{itemize} -The derivations in Chapters~\ref{chap:Functors,-contrafunctors,-and}\textendash \ref{chap:monad-transformers} -of this book follow this pattern. One can show that this pattern holds -for at least \emph{eleven} sub-categories of endofunctors used in -FP practice: functors, contrafunctors, filterable functors, filterable -contrafunctors, applicative functors, applicative contrafunctors, -monads, comonads, traversable functors, monad transformers, and comonad -transformers. - -It appears that category theory and its basic tools (functors, natural -transformations, commutative diagrams) provide a powerful and versatile -language for reasoning about laws of various FP idioms. By invoking -category theory, programmers avoid having to memorize a large number -of laws and constructions. Without the underlying categorical justification, -the laws for different endofunctors will appear to be chosen arbitrarily, -with no clearly recognizable system or pattern. - -In addition, category theory guides programmers in creating highly -abstract libraries that work uniformly with all endofunctors of a -certain sub-category. In programmer\textsf{'}s terms, such libraries contain -functions parameterized by type constructors satisfying appropriate -constraints. Examples are functions that define the product or the -co-product of any two given functors, or define a free monad on a -given functor. Implementing libraries of that kind requires formulating -and verifying the relevant laws. Category theory is a reliable foundation -for such libraries. diff --git a/sofp-src/sofp-filterable.lyx b/sofp-src/sofp-filterable.lyx index df7d2f6d9..750253a6f 100644 --- a/sofp-src/sofp-filterable.lyx +++ b/sofp-src/sofp-filterable.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -430,7 +445,7 @@ filter \end_inset - is the following calculation, + is the following calculation: \begin_inset Formula \[ \sum_{x\in\mathbb{Z};\,0\leq x\leq100;\,\cos x>0}\sqrt{\cos\left(x\right)}\approx38.71\quad. @@ -857,7 +872,7 @@ F[_] \end_inset - can be written as + can be written as: \begin_inset listings inline false status open @@ -1128,7 +1143,7 @@ infinite disjunction \begin_inset Quotes erd \end_inset -, +: \begin_inset Formula \begin{equation} \text{List}^{T}=1+T+T\times T+T\times T\times T+...\label{eq:list-infinite-disjunction} @@ -1205,7 +1220,7 @@ p \end_inset and will be removed from the collection. - The example + This example: \begin_inset listings inline false status open @@ -1842,7 +1857,7 @@ val result1 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewritten via method chains: \end_layout \begin_layout Plain Layout @@ -1912,7 +1927,7 @@ val result2 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewritten via method chains: \end_layout \begin_layout Plain Layout @@ -2098,7 +2113,7 @@ val result1 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewritten via method chains: \end_layout \begin_layout Plain Layout @@ -2167,7 +2182,7 @@ val result2 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewritten via method chains: \end_layout \begin_layout Plain Layout @@ -2229,7 +2244,7 @@ x \end_inset -, the filtering operation +, the filtering call \begin_inset listings inline true status open @@ -2242,7 +2257,7 @@ xs.filter(p) \end_inset will never discard any values. - So we expect the result to remain the same if we + So, we expect the result to remain the same if we \emph on delete \emph default @@ -2267,7 +2282,7 @@ if true \end_inset from a functor block program. - The corresponding code equivalence can be written as + The corresponding code equivalence can be written as: \begin_inset listings inline false status open @@ -2391,7 +2406,7 @@ factory \begin_inset Quotes erd \end_inset - for partial functions, + for partial functions: \begin_inset listings inline false status open @@ -2506,7 +2521,7 @@ true \begin_inset Formula $f_{|p}$ \end_inset - as + as: \begin_inset Formula \begin{equation} (f^{:A\rightarrow B})_{|p}\triangleq x^{:A}\rightarrow p(x)\triangleright\,\begin{array}{|c||c|} @@ -2643,7 +2658,7 @@ val result1 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewritten via method chains: \end_layout \begin_layout Plain Layout @@ -2703,7 +2718,7 @@ val result2 = for { \begin_layout Plain Layout - y = fp(x) // Defined as val fp = if_p(p)(f) + y = fp(x) // Here fp = if_p(p)(f) \end_layout \begin_layout Plain Layout @@ -2713,7 +2728,7 @@ val result2 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewritten via method chains: \end_layout \begin_layout Plain Layout @@ -2796,31 +2811,39 @@ filter : \begin_inset Formula \[ -\text{filt}_{F}:(A\rightarrow\bbnum 2)\rightarrow F^{A}\rightarrow F^{A}\quad, +\text{filt}_{F}:(A\rightarrow\bbnum 2)\rightarrow F^{A}\rightarrow F^{A}\quad. \] \end_inset -and its +The \begin_inset Formula $4$ \end_inset laws (called the naturality, identity, composition, and partial function - laws of -\begin_inset listings -inline true -status open + laws) are formulated for arbitrary functions +\begin_inset Formula $f^{:A\rightarrow B}$ +\end_inset -\begin_layout Plain Layout +, +\begin_inset Formula $p^{:A\rightarrow\bbnum 2}$ +\end_inset -filter -\end_layout +, +\begin_inset Formula $p_{1}^{:A\rightarrow\bbnum 2}$ +\end_inset +, +\begin_inset Formula $p_{2}^{:A\rightarrow\bbnum 2}$ +\end_inset + +, and +\begin_inset Formula $q^{:B\rightarrow\bbnum 2}$ \end_inset -) are: +: \begin_inset Index idx -status open +status collapsed \begin_layout Plain Layout composition law!of @@ -2832,7 +2855,7 @@ filter \begin_inset Index idx -status open +status collapsed \begin_layout Plain Layout naturality law!of @@ -2844,7 +2867,7 @@ filter \begin_inset Index idx -status open +status collapsed \begin_layout Plain Layout identity laws!of @@ -2856,7 +2879,7 @@ filter \begin_inset Index idx -status open +status collapsed \begin_layout Plain Layout partial function law!of @@ -2869,10 +2892,10 @@ filter \begin_inset Formula \begin{align} -\text{naturality law}:\quad & f^{\uparrow F}\bef\text{filt}_{F}(p)=\text{filt}_{F}(f\bef p)\bef f^{\uparrow F}\quad\text{for }\forall(f^{:A\rightarrow B},~p^{:B\rightarrow\bbnum 2})\quad.\label{eq:naturality-law-of-filter}\\ +\text{naturality law}:\quad & f^{\uparrow F}\bef\text{filt}_{F}(q)=\text{filt}_{F}(f\bef q)\bef f^{\uparrow F}\quad.\label{eq:naturality-law-of-filter}\\ \text{identity law}:\quad & \text{filt}_{F}(\_\rightarrow\text{true})=\text{id}^{:F^{A}\rightarrow F^{A}}\quad.\label{eq:identity-law-of-filter}\\ -\text{composition law}:\quad & \text{filt}_{F}(p_{1})\bef\text{filt}_{F}(p_{2})=\text{filt}_{F}(x\rightarrow p_{1}(x)\wedge p_{2}(x))\quad\text{for }\forall(p_{1}^{:A\rightarrow\bbnum 2},~p_{2}^{:A\rightarrow\bbnum 2})\quad.\label{eq:composition-law-of-filter}\\ -\text{partial function law}:\quad & \text{filt}_{F}(p)\bef f^{\uparrow F}=\text{filt}_{F}(p)\bef f_{|p}^{\uparrow F}\quad\text{for }\forall(f^{:A\rightarrow B},~p^{:A\rightarrow\bbnum 2})\quad.\label{eq:partial-function-law-of-filter} +\text{composition law}:\quad & \text{filt}_{F}(p_{1})\bef\text{filt}_{F}(p_{2})=\text{filt}_{F}(x\rightarrow p_{1}(x)\wedge p_{2}(x))\quad.\label{eq:composition-law-of-filter}\\ +\text{partial function law}:\quad & \text{filt}_{F}(p)\bef f^{\uparrow F}=\text{filt}_{F}(p)\bef f_{|p}^{\uparrow F}\quad.\label{eq:partial-function-law-of-filter} \end{align} \end_inset @@ -2890,24 +2913,16 @@ filter \end_inset : -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset Formula \[ -\xymatrix{\xyScaleY{1.4pc}\xyScaleX{7.0pc}F^{A}\ar[r]\sp(0.5){\text{filt}_{F}(f^{:A\rightarrow B}\bef p^{:B\rightarrow\bbnum 2})}\ar[d]\sb(0.45){(f^{:A\rightarrow B})^{\uparrow F}} & F^{A}\ar[d]\sp(0.45){(f^{:A\rightarrow B})^{\uparrow F}}\\ -F^{B}\ar[r]\sp(0.5){\text{filt}_{F}(p^{:B\rightarrow\bbnum 2})} & F^{B} +\xymatrix{\xyScaleY{1.4pc}\xyScaleX{7.0pc}F^{A}\ar[r]\sp(0.5){\text{filt}_{F}(f^{:A\rightarrow B}\bef q^{:B\rightarrow\bbnum 2})}\ar[d]\sb(0.45){(f^{:A\rightarrow B})^{\uparrow F}} & F^{A}\ar[d]\sp(0.45){(f^{:A\rightarrow B})^{\uparrow F}}\\ +F^{B}\ar[r]\sp(0.5){\text{filt}_{F}(q^{:B\rightarrow\bbnum 2})} & F^{B} } \] \end_inset -\begin_inset VSpace -50baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -2919,11 +2934,11 @@ A functor \series bold filterable \series default - if there exists a function + if there is a function \begin_inset Formula $\text{filt}_{F}$ \end_inset - satisfying these four laws. + satisfying these laws. \begin_inset Index idx status open @@ -3162,7 +3177,7 @@ def checkFilteringLaws[F[_] : Filterable : Functor, A, B](implicit \begin_layout Plain Layout - forAll { (f: A => B, p: B => Boolean, fa: F[A]) => // Naturality + forAll { (f: A => B, p: B => Boolean, fa: F[A]) => // Naturality law. \end_layout @@ -3178,7 +3193,7 @@ def checkFilteringLaws[F[_] : Filterable : Functor, A, B](implicit \begin_layout Plain Layout - forAll { (p1: B => Boolean, p2: B => Boolean, fa: F[B]) => // Composition + forAll { (p1: B => Boolean, p2: B => Boolean, fa: F[B]) => // Composition law. \end_layout @@ -3194,7 +3209,7 @@ def checkFilteringLaws[F[_] : Filterable : Functor, A, B](implicit \begin_layout Plain Layout - forAll { (fb: F[B]) => fb.filter(_ => true) shouldEqual fb } // Identity + forAll { (fb: F[B]) => fb.filter(_ => true) shouldEqual fb } // Identity law. \end_layout @@ -3204,8 +3219,8 @@ def checkFilteringLaws[F[_] : Filterable : Functor, A, B](implicit \begin_layout Plain Layout - forAll { (f: A => B, p: A => Boolean, fa: F[A]) => // Partial - function law. + forAll { (f: A => B, p: A => Boolean, fa: F[A]) => // Partial function + law. \end_layout \begin_layout Plain Layout @@ -3272,8 +3287,7 @@ implicit val filterableOrders = new Filterable[Orders] { \begin_layout Plain Layout -checkFilteringLaws[Orders, Int, String] // Need to set type parameters - to specific types. +checkFilteringLaws[Orders, Int, String] // Need to set type parameters. \end_layout \end_inset @@ -3493,20 +3507,19 @@ scala> for { x <- Orders(Some(500), Some(2000)) \begin_layout Plain Layout - if x < 1000 // Intuition says that values of x must be below - 1000 from now on. + if x < 1000 // Intuition says that values of x must be below 1000 from + now on. \end_layout \begin_layout Plain Layout - y = s"Amount: $x" // So, the value x = 2000 should never appear in - this line. + y = s"Amount: $x" // So, the value x = 2000 should never appear in this + line. \end_layout \begin_layout Plain Layout -} yield y // But the final result does not correspond to this - intuition: +} yield y // But the final result does not correspond to this intuition: \end_layout \begin_layout Plain Layout @@ -3939,12 +3952,12 @@ Boolean \end_layout \begin_layout Subsection -Solved examples: Programming with filterable functors +Examples: Programming with filterable functors \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -4027,31 +4040,6 @@ filter \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "49col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace 5baselineskip% -\end_inset - - -\begin_inset Note Comment -status open - -\begin_layout Plain Layout --75baselineskip% if inside the page, 25 if at page start -\end_layout - -\end_inset - - \begin_inset listings lstparams "numbers=left" inline false @@ -4084,23 +4072,6 @@ def filter[A](p: A => Boolean): F[A] => F[A] = { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -125baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In line \begin_inset space ~ \end_inset @@ -4940,7 +4911,21 @@ In the second case, we have two credentials \begin_inset Formula $a_{2}$ \end_inset -, which we can summarize as a table: +. + We can summarize all results in a table, where we denote for brevity the + left-hand side of the composition law by +\begin_inset Formula $\text{L.H.S.}\triangleq s\triangleright\text{filt}_{F}(p_{1})\triangleright\text{filt}_{F}(p_{2})$ +\end_inset + + and the right-hand side by +\begin_inset Formula $\text{R.H.S.}\triangleq s\triangleright\text{filt}_{F}(p_{12})$ +\end_inset + +, where +\begin_inset Formula $p_{12}\triangleq x\rightarrow p_{1}(x)\wedge p_{2}(x)$ +\end_inset + +: \end_layout \begin_layout Standard @@ -5063,7 +5048,7 @@ In the second case, we have two credentials \begin_layout Plain Layout \size footnotesize -\begin_inset Formula $s\triangleright\text{filt}_{F}(p_{1})\triangleright\text{filt}_{F}(p_{2})$ +\begin_inset Formula $\text{L.H.S.}$ \end_inset @@ -5077,7 +5062,7 @@ In the second case, we have two credentials \begin_layout Plain Layout \size footnotesize -\begin_inset Formula $s\triangleright\text{filt}_{F}(p_{12})$ +\begin_inset Formula $\text{R.H.S.}$ \end_inset @@ -5982,13 +5967,8 @@ false \end_layout \begin_layout Standard -Here we denoted -\begin_inset Formula $p_{12}\triangleq x\rightarrow p_{1}(x)\wedge p_{2}(x)$ -\end_inset - - for brevity. - We see that the last two columns are always equal, which verifies the compositi -on law. +We see that the last two columns are always equal, which verifies the compositio +n law. We omitted some rows from the table because the filtering code is completely symmetric with respect to interchanging \begin_inset Formula $a_{1}$ @@ -6070,7 +6050,19 @@ false \end_layout \begin_layout Standard -So, we have seen that all filtering laws hold. +So, we have proved that all filtering laws hold for the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +filter +\end_layout + +\end_inset + + function shown above. If the program's requirements change, the \begin_inset listings inline true @@ -6083,9 +6075,9 @@ filter \end_inset - function will need to be implemented differently. - For instance, the first server might be the only source of credentials: - the second server may copy the first server's credentials if needed, but + function will need to be changed. + For instance, suppose the first server is now the only source of credentials. + The second server may copy the first server's credentials if needed, but the cluster will go down whenever the first server's credentials expire. This corresponds to the code: \end_layout @@ -6123,7 +6115,7 @@ def filter[A](p: A => Boolean): F[A] => F[A] = { \begin_layout Plain Layout case (false, _) => None // The cluster is down if credentials - expired for server 1. + expire for server 1. \end_layout \begin_layout Plain Layout @@ -6161,14 +6153,14 @@ def filter[A](p: A => Boolean): F[A] => F[A] = { \begin_layout Plain Layout - case (true, true) => Some((a1, a2)) // Both credentials - are valid. + case (true, true) => Some((a1, a2)) // Both credentials are + valid. \end_layout \begin_layout Plain Layout - case _ => None // The cluster is down if any credentia -ls expired. + case _ => None // The cluster is down when any + of the credentials expire. \end_layout \begin_layout Plain Layout @@ -6269,7 +6261,7 @@ status open \begin_layout Plain Layout -sealed trait JohnsCoupons[A] // This represents the type $ +sealed trait JohnsCoupons[A] // This represents the type $ \backslash color{dkgreen} \backslash @@ -6427,8 +6419,7 @@ def filterJill[A](p: A => Boolean): JillsCoupons[A] => JillsCoupons[A] = \begin_layout Plain Layout - case Jill0() => Jill0() // We must remove each invalid coupon but - keep the rest. + case Jill0() => Jill0() // We must remove all the invalid coupons. \end_layout \begin_layout Plain Layout @@ -6970,11 +6961,15 @@ noprefix "false" \begin_inset Formula $F$ \end_inset - is equivalent to -\begin_inset Formula $F^{A}\cong\text{Int}\times\left(\bbnum 1+A+A\times A+A\times A\times A\right)=\text{Int}\times\text{JohnsCoupons}^{A}$ + can be written equivalently as: +\begin_inset Formula +\[ +F^{A}\cong\text{Int}\times\left(\bbnum 1+A+A\times A+A\times A\times A\right)=\text{Int}\times\text{JohnsCoupons}^{A}\quad, +\] + \end_inset -, where we used the functor +where we used the functor \begin_inset listings inline true status open @@ -7001,7 +6996,7 @@ noprefix "false" \end_inset . - So we use the same filtering operation for + So, we use the same filtering operation for \begin_inset listings inline true status open @@ -7352,15 +7347,14 @@ filter \end_inset - e.g., like this, + e.g., like this: \begin_inset listings inline false status open \begin_layout Plain Layout -type F[A] = Either[Z, (Int, Z, A, A)] // The type `Z` must be - already defined. +type F[A] = Either[Z, (Int, Z, A, A)] // The type `Z` must be already defined. \end_layout \begin_layout Plain Layout @@ -7381,7 +7375,7 @@ def filter[A](p: A => Boolean): F[A] => F[A] = { \begin_layout Plain Layout else Left(z) // If anything fails the - filter, use `z`. + filter condition, use the value `z`. \end_layout \begin_layout Plain Layout @@ -8182,54 +8176,17 @@ We can always convert a value of type \end_inset is a functor): -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "54col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset listings inline false status open \begin_layout Plain Layout -def inflate[F[_]: Functor, A]: F[A] => F[Option[A]] = -\end_layout - -\begin_layout Plain Layout - - _.map(x => Some(x)) -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -120baselineskip% -\end_inset - - +def inflate[F[_]: Functor, A]: F[A] => F[Option[A]] = _.map(x => Some(x)) \end_layout \end_inset - -\end_layout - -\begin_layout Standard -\noindent The code notation for the function \begin_inset listings inline true @@ -8243,10 +8200,6 @@ inflate \end_inset is: -\begin_inset VSpace -40baselineskip% -\end_inset - - \begin_inset Formula \[ \text{inflate}^{F,A}\triangleq(x^{:A}\rightarrow\bbnum 0^{:\bbnum 1}+x)^{\uparrow F}\quad. @@ -8267,7 +8220,7 @@ It remains to convert \emph on somehow \emph default - do that, say, via a function we call + do that, say, via a function we will call \begin_inset listings inline true status open @@ -8281,21 +8234,6 @@ deflate \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "54col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -8307,27 +8245,7 @@ def deflate[F[_], A]: F[Option[A]] => F[A] = ??? \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -\begin_inset VSpace -50baselineskip% -\end_inset - - +or, in the code notation: \begin_inset Formula \[ \text{deflate}^{F,A}:F^{\bbnum 1+A}\rightarrow F^{A}\quad, @@ -8335,7 +8253,7 @@ def deflate[F[_], A]: F[Option[A]] => F[A] = ??? \end_inset -we would then express +we would then be able to express \begin_inset listings inline true status open @@ -8375,10 +8293,6 @@ deflate \end_inset like this: -\begin_inset VSpace -30baselineskip% -\end_inset - - \begin_inset Formula \begin{align*} & \quad\quad\quad\quad\left(\text{filt}_{F}(p)\right)^{:F^{A}\rightarrow F^{A}}=\text{inflate}\bef\big(\text{filt}_{\text{Opt}}(p)\big)^{\uparrow F}\bef\text{deflate}\quad.\\ @@ -8431,10 +8345,6 @@ We notice that both functions in the composition \end_inset , and so we can simplify that composition to a single lifted function: -\begin_inset VSpace -35baselineskip% -\end_inset - - \begin_inset Formula \begin{align*} & \gunderline{\text{inflate}}\bef(\text{filt}_{\text{Opt}}(p))^{\uparrow F}\\ @@ -8453,21 +8363,6 @@ We will need to use this function often, so let us call it \end_inset , for convenience: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -8484,27 +8379,7 @@ def psi[A](p: A => Boolean): A => Option[A] = \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -125baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -\begin_inset VSpace -140baselineskip% -\end_inset - - +In the code notation: \begin_inset Formula \begin{align*} & \psi^{A}:(A\rightarrow\bbnum 2)\rightarrow A\rightarrow\bbnum 1+A\quad,\\ @@ -8514,10 +8389,6 @@ def psi[A](p: A => Boolean): A => Option[A] = \end_inset -\begin_inset VSpace -150baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -8539,10 +8410,6 @@ filter \end_inset operation as: -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset Formula \begin{align} & \xymatrix{\xyScaleX{4pc}F^{A}\ar[r]\sp(0.5){\psi_{p}^{\uparrow F}} & F^{\bbnum 1+A}\ar[r]\sp(0.55){\text{deflate}} & F^{A}} @@ -8552,10 +8419,6 @@ filter \end_inset -\begin_inset VSpace -150baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -8694,7 +8557,7 @@ deflate \begin_inset Formula $F$ \end_inset -: use +: just use \begin_inset Formula $F$ \end_inset @@ -8727,21 +8590,6 @@ Option \end_inset . -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings lstparams "mathescape=true" inline false @@ -8749,12 +8597,7 @@ status open \begin_layout Plain Layout -def deflate[F[_]: Filterable : Functor, A]: -\end_layout - -\begin_layout Plain Layout - - F[Option[A]] => F[A] = +def deflate[F[_]: Filterable : Functor, A]: F[Option[A]] => F[A] = \end_layout \begin_layout Plain Layout @@ -8765,26 +8608,6 @@ def deflate[F[_]: Filterable : Functor, A]: \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -125baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -\begin_inset VSpace -180baselineskip% -\end_inset - - \begin_inset Formula \begin{align} & \text{deflate}:\xymatrix{\xyScaleX{4.5pc}F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{filt}_{F}(\text{nonEmpty)}} & F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{get}^{\uparrow F}} & F^{A}} @@ -9111,7 +8934,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -9175,7 +8998,7 @@ status open \begin_layout Plain Layout -type F[A] = Z => List[A] // The type Z should have been defined before. +type F[A] = Z => List[A] // The type Z must be defined before. \end_layout \begin_layout Plain Layout @@ -9332,8 +9155,7 @@ status open \begin_layout Plain Layout -type F[A] = Either[(A, A), Z => Z] // The type Z should have been defined - before. +type F[A] = Either[(A, A), Z => Z] // The type Z must be defined before. \end_layout \begin_layout Plain Layout @@ -9939,7 +9761,7 @@ status open \begin_layout Plain Layout -def nonEmpty[A]: Option[A] => Option[Unit] = _.map(_ => ()) // Option[Unit] +def nonEmpty[A]: Option[A] => Option[Unit] = _.map(_ => ()) // Option[Unit] $ \backslash color{dkgreen} @@ -10027,7 +9849,7 @@ status open \begin_layout Plain Layout def psi[A](p: A => Option[Unit]): A => Option[A] = x => p(x).map(_ => x) - // Option[Unit] $ + // Option[Unit] $ \backslash color{dkgreen} \backslash @@ -10150,13 +9972,13 @@ psi(p)(x).get // Expand the code for `psi` and `get`: \begin_layout Plain Layout - == p(x) match { case true => x } // Rewrite this code equivalently + == p(x) match { case true => x } // Rewrite this code equivalently as \end_layout \begin_layout Plain Layout - == x match { case x if p(x) => x } // $ + == x match { case x if p(x) => x } // $ \backslash color{dkgreen}x \backslash @@ -10461,15 +10283,37 @@ filter \end_inset - has that form, so we can try -\end_layout - -\begin_layout Standard + has that form, so we can try deriving a similar naturality law for +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +deflate +\end_layout + +\end_inset + +. + To switch the order of composition of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +deflate +\end_layout + +\end_inset + + with a lifted function, the law must have the form: \begin_inset Wrap figure lines 0 placement L overhang 0in -width "25col%" +width "30col%" status open \begin_layout Plain Layout @@ -10487,7 +10331,7 @@ status open \end_inset -\begin_inset VSpace -150baselineskip% +\begin_inset VSpace 30baselineskip% \end_inset @@ -10499,33 +10343,14 @@ status open \end_layout \begin_layout Standard -\noindent -deriving a similar naturality law for -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -deflate -\end_layout - +\begin_inset space ~ \end_inset -. - To switch the order of composition of -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -deflate -\end_layout +\begin_inset VSpace -45baselineskip% \end_inset - with a lifted function, the law must have the form: + \begin_inset Formula \[ \text{deflate}\bef f^{\uparrow F}=(\text{???})^{\uparrow F}\bef\text{deflate}\quad, @@ -10533,8 +10358,8 @@ deflate \end_inset -as illustrated by the type diagram at left. - The types match in the right-hand side only if the argument of +as illustrated by the type diagram on the left. + The types will match in the right-hand side only if the argument of \begin_inset listings inline true status open @@ -10552,12 +10377,15 @@ deflate \end_inset . - So, the law must have the form -\begin_inset Formula $\text{deflate}\bef f^{\uparrow F}=(\text{???}^{:\bbnum 1+A\rightarrow\bbnum 1+B})^{\uparrow F}\bef\text{deflate}$ + So, the law must have the form: +\begin_inset Formula +\[ +\text{deflate}\bef f^{\uparrow F}=(\text{???}^{:\bbnum 1+A\rightarrow\bbnum 1+B})^{\uparrow F}\bef\text{deflate}\quad. +\] + \end_inset -. - The typed hole +The typed hole \begin_inset Formula $\text{???}^{:\bbnum 1+A\rightarrow\bbnum 1+B}$ \end_inset @@ -10596,27 +10424,7 @@ Option \end_inset . - -\end_layout - -\begin_layout Standard -\begin_inset Newpage newpage -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset Note Comment -status open - -\begin_layout Plain Layout -This sentence is always on the previous page unless we force a page break. -\end_layout - -\end_inset - -So, the + So, the \series bold naturality law \series default @@ -10653,11 +10461,11 @@ deflate lines 0 placement L overhang 0in -width "25col%" +width "30col%" status open \begin_layout Plain Layout -\begin_inset VSpace -165baselineskip% +\begin_inset VSpace -200baselineskip% \end_inset @@ -10721,19 +10529,7 @@ noprefix "false" \end_inset -) holds for -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -deflate -\end_layout - -\end_inset - -, we continue the derivation in Eq. +) holds, we continue the derivation in Eq. \begin_inset space ~ \end_inset @@ -10924,12 +10720,12 @@ where we expanded the matrix to accommodate the disjunctive type Then we compute: \begin_inset Formula \begin{align*} - & x^{:\bbnum 1+A}\triangleright\psi_{\text{nonEmpty}}=\text{nonEmpty}\left(x\right)\triangleright\,\begin{array}{|c||cc|} + & x^{:\bbnum 1+A}\triangleright\psi_{\text{nonEmpty}}=\gunderline{\text{nonEmpty}\left(x\right)}\triangleright\,\begin{array}{|c||cc|} & \bbnum 1 & \bbnum 1+A\\ \hline \bbnum 1 & \text{id} & \bbnum 0\\ \bbnum 1 & \bbnum 0 & 1\rightarrow x \end{array}\\ -\text{definition~(\ref{eq:def-of-nonEmpty-and-get}) of }\text{nonEmpty}:\quad & =x\triangleright\,\begin{array}{|c||cc|} +\text{definition~(\ref{eq:def-of-nonEmpty-and-get})}:\quad & =x\triangleright\,\begin{array}{|c||cc|} & \bbnum 1 & \bbnum 1\\ \hline \bbnum 1 & \text{id} & \bbnum 0\\ A & \bbnum 0 & \_\rightarrow1 @@ -10946,21 +10742,21 @@ A & \bbnum 0 & \_\rightarrow x & \bbnum 1 & \bbnum 1 & A\\ \hline \bbnum 1 & \text{id} & \bbnum 0 & \bbnum 0\\ A & \bbnum 0 & \bbnum 0 & \text{id} -\end{array}\quad, +\end{array}\quad. \end{align*} \end_inset -where the last matrix uses the fact that +The last matrix was derived using the fact that \begin_inset Formula $x$ \end_inset - matches the type + matches the bottom row where the type is \begin_inset Formula $\bbnum 0+A$ \end_inset - in the bottom row. - Finally, +. + Finally: \begin_inset Formula \begin{align} & x^{:\bbnum 1+A}\triangleright\psi_{\text{nonEmpty}}\bef\text{get}^{\uparrow\text{Opt}}=x\triangleright\,\begin{array}{|c||ccc|} @@ -11174,7 +10970,7 @@ Proof \end_layout \begin_layout Standard -Assume that a +Given a \begin_inset listings inline true status open @@ -11186,7 +10982,7 @@ deflate \end_inset - function is given, and define + function, define \begin_inset listings inline true status open @@ -11234,7 +11030,7 @@ noprefix "false" \begin_layout Standard \begin_inset Formula \[ -\xymatrix{\xyScaleY{0.1pc}\xyScaleX{3pc} & F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{deflate}} & F^{A}\ar[rd]\sp(0.5){~(f^{:A\rightarrow B})^{\uparrow F}}\\ +\xymatrix{\xyScaleY{0.1pc}\xyScaleX{2.8pc} & F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{deflate}} & F^{A}\ar[rd]\sp(0.5){~(f^{:A\rightarrow B})^{\uparrow F}}\\ F^{A}\ar[ru]\sp(0.5){\psi_{p}^{\uparrow F}}\ar[rd]\sb(0.5){\psi_{p}^{\uparrow F}} & & & F^{B} & \psi_{p}^{\uparrow F}\bef\text{deflate}\bef f^{\uparrow F}=\psi_{p}^{\uparrow F}\bef\text{deflate}\bef f_{|p}^{\uparrow F}\quad.\\ & F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{deflate}} & F^{A}\ar[ru]\sb(0.5){(f_{|p}^{:A\rightarrow B})^{\uparrow F}} } @@ -11351,7 +11147,7 @@ psi(p)(x).map(f) == (p(x) match { The same calculation in the code notation looks like this: \begin_inset Formula \begin{align*} - & x^{:A}\triangleright\psi_{p}\bef f^{\uparrow\text{Opt}}=p(x)\triangleright\,\begin{array}{|c||cc|} +x^{:A}\triangleright\psi_{p}\bef f^{\uparrow\text{Opt}} & =p(x)\triangleright\,\begin{array}{|c||cc|} & \bbnum 1 & A\\ \hline \bbnum 1\,(\text{false}) & \text{id} & \bbnum 0\\ \bbnum 1\,(\text{true}) & \bbnum 0 & 1\rightarrow x @@ -11359,7 +11155,8 @@ The same calculation in the code notation looks like this: & \bbnum 1 & A\\ \hline \bbnum 1 & \text{id} & \bbnum 0\\ A & \bbnum 0 & f -\end{array}\,=p(x)\triangleright\,\begin{array}{|c||cc|} +\end{array}\\ + & =p(x)\triangleright\,\,\begin{array}{|c||cc|} & \bbnum 1 & A\\ \hline \bbnum 1\,(\text{false}) & \text{id} & \bbnum 0\\ \bbnum 1\,(\text{true}) & \bbnum 0 & 1\rightarrow f(x) @@ -12115,10 +11912,6 @@ liftOpt and denote by \begin_inset Formula $\text{liftOpt}_{F}$ -\end_inset - - or simply by -\begin_inset Formula $\text{liftOpt}$ \end_inset : @@ -12324,32 +12117,7 @@ deflate \end_inset - is -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "28col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -185baselineskip% -\end_inset - - -\begin_inset Note Comment -status open - -\begin_layout Plain Layout --200baselineskip% if inside the page, -85 if at start of page -\end_layout - -\end_inset - - + is illustrated by the diagram: \begin_inset Formula \[ \xymatrix{\xyScaleY{1.4pc}\xyScaleX{4.4pc}F^{A}\ar[r]\sp(0.5){(f^{:A\rightarrow\bbnum 1+B})^{\uparrow F}}\ar[rd]\sb(0.4){\text{liftOpt}\,(f)\triangleq~~} & F^{\bbnum 1+B}\ar[d]\sp(0.4){\text{deflate}}\\ @@ -12359,22 +12127,7 @@ status open \end_inset - -\begin_inset VSpace -200baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -illustrated in the diagram at left. - Since +Since \begin_inset Formula $f$ \end_inset @@ -12400,7 +12153,7 @@ noprefix "false" \begin_inset Formula $A=\bbnum 1+B$ \end_inset - to find + to find: \begin_inset Formula \begin{equation} \text{liftOpt}^{\bbnum 1+B,B}(\text{id}^{:\bbnum 1+B\rightarrow\bbnum 1+B})=\gunderline{\text{id}^{\uparrow F}}\bef\text{deflate}=\text{deflate}\quad.\label{eq:def-deflate-via-liftOpt} @@ -12994,21 +12747,6 @@ deflate \end_inset -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "24col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -250baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{1.8pc}\xyScaleX{4.5pc}F^{A}\ar[d]\sb(0.5){h^{\uparrow F}}\ar[rd]\sp(0.5){~~\text{ liftOpt}\,(h\bef f)}\\ @@ -13018,20 +12756,6 @@ F^{B}\ar[r]\sp(0.42){\text{liftOpt}\,(f)} & F^{C} \end_inset - -\begin_inset VSpace -200baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent It follows that \emph on if @@ -13084,7 +12808,7 @@ liftOpt \end_inset - (see diagram at left) + (see diagram at left): \begin_inset Formula \begin{equation} (h^{:A\rightarrow B})^{\uparrow F}\bef\text{liftOpt}_{F}^{B,C}(f^{:B\rightarrow\bbnum 1+C})=\text{liftOpt}_{F}^{A,C}(h^{:A\rightarrow B}\bef f^{:B\rightarrow\bbnum 1+C})\quad.\label{eq:left-naturality-law-of-liftOpt} @@ -13123,7 +12847,7 @@ noprefix "false" \end_inset -), +): \begin_inset Formula \begin{align*} \text{expect to equal }\text{liftOpt}\,(f):\quad & \text{liftOpt}^{\prime}(f)=\gunderline{f^{\uparrow F}}\bef\text{liftOpt}\,(\gunderline{\text{id}})=\text{liftOpt}\,(\gunderline{f\bef\text{id}})=\text{liftOpt}\,(f)\quad. @@ -13431,7 +13155,7 @@ filter \end_inset - like this, + like this: \begin_inset Formula \begin{equation} \text{liftOpt}^{A,B}(f^{:A\rightarrow\bbnum 1+B})\triangleq f^{\uparrow F}\bef\text{deflate}=f^{\uparrow F}\bef\text{filt}\,(\psi_{\text{nonEmpty}})\quad,\label{eq:def-liftOpt-via-filter} @@ -13544,7 +13268,7 @@ x => Some(x) \end_inset - in Scala), + in Scala): \begin_inset Formula \begin{align} \text{use Eq.~(\ref{eq:def-of-psi})}:\quad & x^{:A}\triangleright\psi_{(\_\rightarrow\text{true})}=x^{:A}\triangleright(\_^{:A}\rightarrow\gunderline{\text{true}^{:\text{Opt}^{\bbnum 1}}})\bef(1\rightarrow x)^{\uparrow\text{Opt}}\nonumber \\ @@ -13589,7 +13313,7 @@ liftOpt \end_inset - to be + to be: \begin_inset Formula \begin{equation} \text{liftOpt}^{A,A}(x^{:A}\rightarrow\bbnum 0+x)=\text{id}^{:F^{A}\rightarrow F^{A}}\quad.\label{eq:identity-law-of-liftOpt} @@ -13993,7 +13717,7 @@ noprefix "false" \end_inset -) is written more concisely as +) is written more concisely as: \begin_inset Formula \begin{equation} \text{liftOpt}_{F}(\text{pu}_{\text{Opt}})=\text{id}\quad.\label{eq:identity-law-liftOpt-via-pure-puOpt} @@ -14076,7 +13800,7 @@ liftOpt \end_inset - is + is: \begin_inset Formula \begin{equation} \text{liftOpt}_{F}(f^{:A\rightarrow B}\bef\text{pu}_{\text{Opt}}^{B})=f^{\uparrow F}\quad.\label{eq:combined-naturality-identity-law-of-liftOpt} @@ -14192,7 +13916,7 @@ liftOpt \end_inset . - Begin by writing + Begin by writing: \begin_inset listings inline false status open @@ -14306,7 +14030,7 @@ status open \begin_layout Plain Layout -x:A +x: A \end_layout \end_inset @@ -14436,7 +14160,7 @@ Denote this combination of the functions \begin_inset Formula $\diamond_{_{\text{Opt}}}$ \end_inset -, so that we may write +, so that we may write: \begin_inset Formula \[ \psi_{p}=\psi_{p_{1}}\diamond_{_{\text{Opt}}}\psi_{p_{2}}\triangleq x^{:A}\rightarrow x\triangleright\psi_{p_{1}}\triangleright\text{flm}_{\text{Opt}}(\psi_{p_{2}})=\psi_{p_{1}}\bef(y\rightarrow y\triangleright\text{flm}_{\text{Opt}}(\psi_{p_{2}}))\quad. @@ -14492,7 +14216,7 @@ x.flatMap(f) \begin_inset Formula $\text{flm}_{\text{Opt}}$ \end_inset - as a curried function with the type signature + as a curried function with the type signature: \begin_inset Formula \[ \text{flm}_{\text{Opt}}:(A\rightarrow\text{Opt}^{B})\rightarrow\text{Opt}^{A}\rightarrow\text{Opt}^{B}\quad. @@ -14724,7 +14448,7 @@ filter \end_inset 's composition law holds, we obtain (without any additional assumptions) - the equation + the equation: \begin_inset Formula \begin{equation} \text{liftOpt}\,(\psi_{p_{1}})\bef\text{liftOpt}\,(\psi_{p_{2}})=\text{liftOpt}\,(\psi_{p_{1}}\diamond_{_{\text{Opt}}}\psi_{p_{2}})\quad.\label{eq:restricted-composition-law-for-liftOpt} @@ -14770,31 +14494,6 @@ liftOpt \end_inset . -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "28col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -185baselineskip% -\end_inset - - -\begin_inset Note Comment -status open - -\begin_layout Plain Layout --200 if inside page, -120 if at start of page -\end_layout - -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{1.2pc}\xyScaleX{1.8pc} & F^{B}\ar[rd]\sp(0.55){\ \text{liftOpt}\,(g)}\\ @@ -14804,20 +14503,6 @@ F^{A}\ar[ru]\sp(0.45){\text{liftOpt}\,(f)\ }\ar[rr]\sb(0.5){\text{liftOpt}\,(f\d \end_inset - -\begin_inset VSpace -215baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The composition law of \begin_inset listings inline true @@ -14830,7 +14515,7 @@ liftOpt \end_inset - is then written as + is then written as: \begin_inset Index idx status open @@ -15153,7 +14838,7 @@ Proof \begin_inset Formula $f^{:A\rightarrow\bbnum 1+B}$ \end_inset - to be of the form + to be of the form: \begin_inset Formula \begin{align*} & f^{:A\rightarrow\bbnum 1+B}\triangleq h^{:A\rightarrow B}\bef\text{pu}_{\text{Opt}}^{:B\rightarrow\bbnum 1+B}\\ @@ -15247,7 +14932,7 @@ flatMap \begin_inset Formula $\diamond_{_{\text{Opt}}}$ \end_inset - is simplified to + is simplified to: \begin_inset Formula \begin{equation} f^{:A\rightarrow\bbnum 1+B}\diamond_{_{\text{Opt}}}g^{:B\rightarrow\bbnum 1+C}\triangleq f\bef\text{flm}_{\text{Opt}}(g)\quad.\label{eq:def-of-Kleisli-product} @@ -15263,7 +14948,7 @@ Then we compute \begin_inset Formula $\diamond_{_{\text{Opt}}}$ \end_inset - as + as: \begin_inset Formula \begin{align*} \text{definition of }f:\quad & \gunderline f\diamond_{_{\text{Opt}}}g=(h\bef\text{pu}_{\text{Opt}})\,\gunderline{\diamond_{_{\text{Opt}}}}\,g=h\bef\gunderline{\text{pu}_{\text{Opt}}\bef\text{flm}_{\text{Opt}}}(g)\\ @@ -15487,7 +15172,7 @@ liftOpt \begin_inset Formula $g^{:B\rightarrow\bbnum 1+C}$ \end_inset - to be of the form + to be of the form: \begin_inset Formula \[ g^{:B\rightarrow\bbnum 1+C}\triangleq h^{:B\rightarrow C}\bef\text{pu}_{\text{Opt}}^{C}\quad, @@ -15542,7 +15227,7 @@ To proceed, we need to compute the Kleisli composition \begin_inset Formula $g$ \end_inset -, +: \begin_inset Formula \begin{align*} & f\diamond_{_{\text{Opt}}}\gunderline g=f\diamond_{_{\text{Opt}}}(h\bef\text{pu}_{\text{Opt}})\\ @@ -15590,22 +15275,7 @@ liftOpt \end_inset -, -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "30col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -140baselineskip% -\end_inset - - +: \begin_inset Formula \[ \xymatrix{\xyScaleY{1.4pc}\xyScaleX{4.5pc}F^{A}\ar[d]\sb(0.5){\text{liftOpt}\,(f)}\ar[rd]\sp(0.5){~~~\text{ liftOpt}\,(f\bef h^{\uparrow\text{Opt}})}\\ @@ -15616,26 +15286,6 @@ F^{B}\ar[r]\sp(0.42){h^{\uparrow F}} & F^{C} \end_inset -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset VSpace -60baselineskip% -\end_inset - - -\begin_inset space ~ -\end_inset - - \begin_inset Formula \begin{equation} \text{liftOpt}_{F}(f^{:A\rightarrow\bbnum 1+B})\bef h^{\uparrow F}=\text{liftOpt}_{F}(f\bef h^{\uparrow\text{Opt}})\quad.\label{eq:right-naturality-law-of-liftOpt} @@ -16448,7 +16098,11 @@ The functor \begin_inset Formula $G^{A}$ \end_inset - is any functor. + is +\emph on +any +\emph default + functor. \end_layout \begin_layout Subparagraph @@ -16464,22 +16118,7 @@ Assuming that \begin_inset Formula $\text{liftOpt}_{F}$ \end_inset - as -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "61col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - + as: \begin_inset listings inline false status open @@ -16497,26 +16136,6 @@ def liftOpt_F[A, B](f: A => Option[B]): G[H[A]] => G[H[B]] = \end_inset -\begin_inset VSpace -30baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -90baselineskip% -\end_inset - - \begin_inset Formula \[ \text{liftOpt}_{F}(f)\triangleq\big(\text{liftOpt}_{H}(f)\big)^{\uparrow G}\quad. @@ -16674,7 +16293,7 @@ noprefix "false" \end_inset -), +): \begin_inset Formula \[ \text{liftOpt}_{F}(p)\triangleq\text{liftOpt}_{G}(p)\boxtimes\text{liftOpt}_{H}(p)\quad. @@ -16857,7 +16476,7 @@ Assuming that \begin_inset Formula $\text{liftOpt}_{F}$ \end_inset - as + as: \begin_inset listings inline false status open @@ -16915,7 +16534,7 @@ noprefix "false" \end_inset -, +: \begin_inset Formula \[ f^{\uparrow F}\triangleq\begin{array}{|c||cc|} @@ -17048,7 +16667,7 @@ Assuming that \begin_inset Formula $\text{liftOpt}_{F}$ \end_inset - as + as: \begin_inset listings inline false status open @@ -17067,7 +16686,7 @@ def liftOpt_F[A, B](f: A => Option[B]): Option[(A, G[A])] => Option[(B, \begin_layout Plain Layout - case Some((a, ga)) => f(a) match { // Does `a` pass the filtering + case Some((a, ga)) => f(a) match { // Does `a` satisfy the predicate? \end_layout @@ -17153,7 +16772,7 @@ Option \end_inset - value looks like this function, + value looks like this function: \begin_inset listings inline false status open @@ -17257,7 +16876,7 @@ liftOpt_F \end_inset - as + as: \begin_inset listings inline false status open @@ -17508,7 +17127,7 @@ liftOpt \end_inset -'s composition law, +'s composition law: \begin_inset Formula \begin{equation} \text{liftOpt}_{F}(f)\bef\text{liftOpt}_{F}(f^{\prime})=\text{liftOpt}_{F}(f\diamond_{_{\text{Opt}}}f^{\prime})\quad.\label{eq:liftOpt-composition-law-derivation1} @@ -17687,7 +17306,7 @@ liftOpt \end_inset -'s composition law, +'s composition law: \begin_inset Formula \begin{equation} \text{flm}_{\text{Opt}}(f)\bef\text{flm}_{\text{Opt}}(f^{\prime})=\text{flm}_{\text{Opt}}(f\diamond_{_{\text{Opt}}}f^{\prime})=\text{flm}_{\text{Opt}}\big(f\bef\text{flm}_{\text{Opt}}(f^{\prime})\big)\quad.\label{eq:associativity-law-of-flatMap-for-Option} @@ -17777,7 +17396,7 @@ noprefix "false" \end_inset -), so that we can write +), so that we can write: \begin_inset Formula \begin{equation} r_{f,g}\triangleq b\rightarrow b\times\text{liftOpt}_{G}(f)(g)\quad,\quad\quad\text{liftOpt}_{F}(f)=\text{flm}_{\text{Opt}}(a\times g\rightarrow a\triangleright f\bef r_{f,g}^{\uparrow\text{Opt}})\quad.\label{eq:liftOpt-short-flatmap-opt-derivation1} @@ -17934,7 +17553,7 @@ flatMap \end_inset - that should have the form + that should have the form: \begin_inset Formula \begin{align*} & (p^{:A\rightarrow B})^{\uparrow\text{Opt}}\bef\text{flm}_{\text{Opt}}(q^{:B\rightarrow\text{Opt}^{C}})=\text{flm}_{\text{Opt}}(\text{???}^{:A\rightarrow\text{Opt}^{C}})\quad,\\ @@ -18029,7 +17648,7 @@ noprefix "false" \end_inset -and +and: \begin_inset Formula \begin{align*} & \text{flm}_{\text{Opt}}(f^{\prime})\,\gunderline{\bef r_{f\bef\text{flm}_{\text{Opt}}(f^{\prime}),g}^{\uparrow\text{Opt}}}=\text{flm}_{\text{Opt}}\big(\gunderline{f^{\prime}}\bef r_{f\bef\text{flm}_{\text{Opt}}(f^{\prime}),g}^{\uparrow\text{Opt}}\big)\\ @@ -18038,8 +17657,8 @@ and \end_inset -The difference between sub-expressions has become smaller: it just remains - to show that +The difference between sub-expressions has become smaller; it just remains + to show the following: \begin_inset Formula \[ r_{f^{\prime},\text{liftOpt}_{G}(f)(g)}\overset{?}{=}r_{f\bef\text{flm}_{\text{Opt}}(f^{\prime}),g}\quad. @@ -18075,7 +17694,7 @@ This is equivalent to \begin_inset Formula $g$ \end_inset -, +: \begin_inset Formula \[ g\triangleright\text{liftOpt}_{G}(f)\bef\text{liftOpt}_{G}(f^{\prime})=g\triangleright\text{liftOpt}_{G}(\gunderline{f\diamond_{_{\text{Opt}}}f^{\prime}})=g\triangleright\text{liftOpt}_{G}\big(f\bef\text{flm}_{\text{Opt}}(f^{\prime})\big)\quad. @@ -18132,7 +17751,7 @@ noprefix "false" \begin_inset Formula $G^{\bullet}\triangleq\bbnum 1$ \end_inset - and obtain the type + and obtain the type: \begin_inset Formula \[ L_{n}^{A}\triangleq\underbrace{\bbnum 1+A\times\left(\bbnum 1+A\times\left(\bbnum 1+...\times(\bbnum 1+A\times\bbnum 1)\right)\right)}_{\text{parameter }A\text{ is used }n\text{ times}}\quad, @@ -18229,7 +17848,7 @@ noprefix "false" \end_inset - to the functor + to the functor: \begin_inset Formula \[ F^{A}\triangleq\bbnum 1+\underbrace{A\times A\times...\times A}_{n\text{ times}}\times\,G^{A}\quad. @@ -18444,7 +18063,7 @@ The only way to proceed is to have a function \end_inset . - So, we need to require having a function + So, we need to require having a function: \begin_inset Formula \begin{equation} \text{liftOpt}_{G}(f^{:A\rightarrow\bbnum 1+B}):G^{B}\rightarrow G^{A}\quad.\label{eq:type-signature-liftOpt-contrafunctors} @@ -18692,7 +18311,7 @@ identity laws!of filterable contrafunctors \end_inset - to be + to be: \begin_inset Formula \begin{equation} \text{\text{liftOpt}}_{G}(f\bef\text{pu}_{\text{Opt}})=f^{\downarrow G}\quad.\label{eq:naturality-identity-law-filterable-contrafunctor} @@ -18712,7 +18331,7 @@ The composition law of \begin_inset Formula $p^{:G^{A}\rightarrow H^{A}}$ \end_inset - is + is: \begin_inset Formula \begin{align*} \text{left-hand side of Eq.~(\ref{eq:composition-law-of-liftOpt}) for }F:\quad & p\triangleright\text{liftOpt}_{F}(f)\bef\text{liftOpt}_{F}(g)=\gunderline{p\triangleright\text{liftOpt}_{F}(f)}\triangleright\text{liftOpt}_{F}(g)\\ @@ -18741,7 +18360,7 @@ noprefix "false" \begin_inset Formula $F$ \end_inset - is + is: \begin_inset Formula \[ p\triangleright\text{liftOpt}_{F}(f\diamond_{_{\text{Opt}}}g)=\text{liftOpt}_{G}(f\diamond_{_{\text{Opt}}}g)\bef p\bef\text{liftOpt}_{H}(f\diamond_{_{\text{Opt}}}g)\quad. @@ -18767,7 +18386,7 @@ composition law!of filterable contrafunctor \begin_inset Formula $G$ \end_inset - to be + to be: \begin_inset Formula \begin{equation} \text{\text{liftOpt}}_{G}(g)\bef\text{\text{liftOpt}}_{G}(f)=\text{liftOpt}_{G}(f\diamond_{_{\text{Opt}}}g)\quad.\label{eq:composition-law-filterable-contrafunctor} @@ -18818,7 +18437,7 @@ List \end_inset - functor defined by + functor defined by: \begin_inset Formula \[ \text{List}^{A}\triangleq\bbnum 1+A\times\text{List}^{A}\quad, @@ -18877,7 +18496,7 @@ If \begin_inset Formula $F^{\bullet}$ \end_inset - defined by + defined by: \begin_inset Formula \[ F^{A}\triangleq G^{A}+A\times F^{A} @@ -19268,7 +18887,7 @@ noprefix "false" \begin_inset Formula $\bbnum 0+a\times r$ \end_inset - gives + gives: \begin_inset Formula \begin{align*} & (\bbnum 0+a\times r)\triangleright\text{liftOpt}_{F}(f\diamond_{_{\text{Opt}}}g)\\ @@ -19294,7 +18913,7 @@ noprefix "false" \end_inset -) is +) is: \begin_inset Formula \begin{equation} f\bef\,\begin{array}{||c|} @@ -19532,7 +19151,7 @@ noprefix "false" \begin_inset Formula $\text{liftOpt}_{F}$ \end_inset - recursively as + recursively as: \begin_inset Formula \[ \text{liftOpt}_{F}(f^{:A\rightarrow\bbnum 1+B})\triangleq\text{liftOpt}_{S}(f)\bef\text{bimap}_{S}(\text{id})\big(\overline{\text{liftOpt}_{F}}(f)\big)=\text{liftOpt}_{S}(f)\bef\big(\overline{\text{liftOpt}_{F}}(f)\big)^{\uparrow S^{B,\bullet}}\quad. @@ -19549,7 +19168,7 @@ where \begin_inset Formula $F$ \end_inset - is defined (also recursively) by + is defined (also recursively) by: \begin_inset Formula \[ (f^{:A\rightarrow B})^{\uparrow F}\triangleq\text{bimap}_{S}(f)(\overline{f^{\uparrow F}})=f^{\uparrow S^{\bullet,R}}\bef\big(\overline{f^{\uparrow F}}\big)^{\uparrow S^{B,\bullet}}\quad. @@ -19669,8 +19288,8 @@ S^{B,R^{\prime}}\ar[r]\sp(0.5){~\text{liftOpt}_{S}(g^{:B\rightarrow\bbnum 1+C})} \end_inset -We expect this naturality law to hold automatically for any fully parametric - implementation of +This naturality law will hold automatically for any fully parametric implementat +ion of \begin_inset Formula $\text{liftOpt}_{S}$ \end_inset @@ -19915,7 +19534,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -20087,7 +19706,7 @@ None \begin_inset Formula $d$ \end_inset - must be + must be: \begin_inset listings inline false status open @@ -20127,7 +19746,7 @@ d \begin_inset Formula $C^{A}$ \end_inset -, implemented as +, implemented as: \begin_inset listings inline false status open @@ -20221,7 +19840,7 @@ filter \end_inset - function is + function is: \begin_inset listings inline false status open @@ -20244,10 +19863,6 @@ def filter[A](p: A => Boolean): ((Z, A => Z)) => (Z, A => Z) = { \end_inset -\begin_inset VSpace 15baselineskip% -\end_inset - - \begin_inset Formula \[ \text{filt}_{C}(p^{:A\rightarrow\bbnum 2})\triangleq z^{:Z}\times f^{:A\rightarrow Z}\rightarrow z\times\bigg(a^{:A}\rightarrow p(a)\triangleright\,\begin{array}{|c||c|} @@ -20295,7 +19910,7 @@ filter \end_inset - function is implemented by + function is implemented by: \begin_inset listings inline false status open @@ -20455,7 +20070,7 @@ inflate \end_inset - is + is: \begin_inset Formula \[ \text{inflate}_{C}:C^{A}\rightarrow C^{\bbnum 1+A}\quad. @@ -21054,7 +20669,7 @@ filter \begin_inset Formula $\text{filt}_{C}$ \end_inset - as + as: \begin_inset Formula \begin{equation} \text{filt}_{C}(p^{:A\rightarrow\bbnum 2})\bef(f^{:B\rightarrow A})^{\downarrow C}=f^{\downarrow C}\bef\text{filt}_{C}(f\bef p)\quad.\label{eq:naturality-for-filter-for-contrafunctors} @@ -21077,7 +20692,7 @@ Assuming this law, we find that \end_inset Here we assumed the partial function law in the form similar to that for - functors, + functors: \begin_inset Formula \[ f_{|p}^{\downarrow C}\bef\text{filt}_{C}(p)=f^{\downarrow C}\bef\text{filt}_{C}(p)\quad. @@ -21171,19 +20786,16 @@ inflate \series bold naturality law \series default - for the -\end_layout + for the function +\begin_inset Formula $\text{inflate}_{C}$ +\end_inset -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open +: +\begin_inset Formula +\begin{equation} +(f^{:B\rightarrow A})^{\downarrow C}\bef\text{inflate}_{C}=\text{inflate}_{C}\bef f^{\uparrow\text{Opt}\downarrow C}\quad.\label{eq:naturality-law-of-inflate-for-filterable-contrafunctor} +\end{equation} -\begin_layout Plain Layout -\begin_inset VSpace -200baselineskip% \end_inset @@ -21196,34 +20808,6 @@ status open \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -\begin_inset Formula $\text{inflate}_{C}$ -\end_inset - - function: -\begin_inset Formula -\begin{equation} -(f^{:B\rightarrow A})^{\downarrow C}\bef\text{inflate}_{C}=\text{inflate}_{C}\bef f^{\uparrow\text{Opt}\downarrow C}\quad.\label{eq:naturality-law-of-inflate-for-filterable-contrafunctor} -\end{equation} - -\end_inset - With help of this law, we can finish the derivation: \begin_inset Formula \begin{align*} @@ -21890,10 +21474,10 @@ liftOpt \end_inset operation for -\begin_inset Formula $F^{\bullet}$ +\begin_inset Formula $F$ \end_inset - by + by: \begin_inset listings inline false status open @@ -22064,7 +21648,7 @@ search functor \begin_inset Formula $Z\triangleq\bbnum 1$ \end_inset -, which gives the type constructor +, which gives the type constructor: \begin_inset Formula \[ S_{\bbnum 1}^{A}\triangleq(A\rightarrow\bbnum 2)\rightarrow\bbnum 1+A\quad. @@ -22490,21 +22074,6 @@ def liftOpt_F[A, B](f: A => Option[B]): F[B] => F[A] = { case F(sbfb) => \end_inset -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -190baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{S^{B,F^{B}}\ar[r]\sp(0.525){\text{liftOpt}_{S}(f^{:A\rightarrow\bbnum 1+B})}\ar[rd]\sb(0.45){\text{liftOpt}_{F}(f)\triangleq~~~} & S^{A,F^{B}}\ar[d]\sp(0.45){\big(\overline{\text{liftOpt}_{F}}(f)\big)^{\uparrow S^{A,\bullet}}}\\ @@ -22514,23 +22083,6 @@ status open \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -250baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Note that \begin_inset Formula $F^{B}\cong S^{B,F^{B}}$ \end_inset @@ -22592,7 +22144,7 @@ noprefix "false" \end_inset -) is verified by +) is verified by: \begin_inset Formula \begin{align*} \text{expect to equal }f^{\downarrow F}:\quad & \text{liftOpt}_{F}(f\bef\text{pu}_{\text{Opt}})=\gunderline{\text{liftOpt}_{S}(f\bef\text{pu}_{\text{Opt}})}\bef\big(\overline{\text{liftOpt}_{F}}(f\bef\text{pu}_{\text{Opt}})\big)^{\uparrow S^{A,\bullet}}\\ @@ -22624,68 +22176,30 @@ noprefix "false" \text{law~(\ref{eq:binaturality-law-of-filterable-profunctor}) of }\text{liftOpt}_{S}:\quad & =\gunderline{\text{liftOpt}_{S}(g)\bef\text{liftOpt}_{S}(f)}\bef\big(\overline{\text{liftOpt}_{F}}(g)\big)^{\uparrow S^{A,\bullet}}\bef\big(\overline{\text{liftOpt}_{F}}(f)\big)^{\uparrow S^{A,\bullet}}\\ \text{law~(\ref{eq:composition-law-filterable-contrafunctor}) of }\text{liftOpt}_{S}:\quad & =\text{liftOpt}_{S}(f\diamond_{_{\text{Opt}}}g)\bef\big(\gunderline{\overline{\text{liftOpt}_{F}}(g)\bef\overline{\text{liftOpt}_{F}}(f)}\big)^{\uparrow S^{A,\bullet}}\\ \text{inductive assumption}:\quad & =\text{liftOpt}_{S}(f\diamond_{_{\text{Opt}}}g)\bef\big(\overline{\text{liftOpt}_{F}}(f\diamond_{_{\text{Opt}}}g)\big)^{\uparrow S^{A,\bullet}}=\text{liftOpt}_{F}(f\diamond_{_{\text{Opt}}}g)\quad. -\end{align*} - -\end_inset - -In this derivation, we have used the naturality law of -\begin_inset Formula $\text{liftOpt}_{S}$ -\end_inset - - with respect to lifting in the type parameter -\begin_inset Formula $A$ -\end_inset - - of -\begin_inset Formula $S^{A,R}$ -\end_inset - -: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "30col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -180baselineskip% -\end_inset - - -\begin_inset Formula -\[ -\xymatrix{S^{B,R}\ar[r]\sp(0.5){\text{liftOpt}_{S}(f^{:A\rightarrow\bbnum 1+B})}\ar[d]\sp(0.4){(h^{:R\rightarrow R^{\prime}})^{\uparrow S^{B,\bullet}}} & S^{A,R}\ar[d]\sb(0.4){h^{\uparrow S^{A,\bullet}}}\\ -\xyScaleY{1.8pc}\xyScaleX{6.0pc}S^{B,R^{\prime}}\ar[r]\sp(0.45){\text{liftOpt}_{S}(f)} & S^{A,R^{\prime}} -} -\] - -\end_inset - - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - -\end_layout +\end{align*} \end_inset +In this derivation, we have used the naturality law of +\begin_inset Formula $\text{liftOpt}_{S}$ +\end_inset -\end_layout + with respect to lifting in the type parameter +\begin_inset Formula $A$ +\end_inset -\begin_layout Standard -\begin_inset space ~ + of +\begin_inset Formula $S^{A,R}$ \end_inset +: +\begin_inset Formula +\[ +\xymatrix{S^{B,R}\ar[r]\sp(0.5){\text{liftOpt}_{S}(f^{:A\rightarrow\bbnum 1+B})}\ar[d]\sp(0.4){(h^{:R\rightarrow R^{\prime}})^{\uparrow S^{B,\bullet}}} & S^{A,R}\ar[d]\sb(0.4){h^{\uparrow S^{A,\bullet}}}\\ +\xyScaleY{1.8pc}\xyScaleX{6.0pc}S^{B,R^{\prime}}\ar[r]\sp(0.45){\text{liftOpt}_{S}(f)} & S^{A,R^{\prime}} +} +\] -\begin_inset VSpace -140baselineskip% \end_inset @@ -22921,12 +22435,12 @@ filter \end_layout \begin_layout Subsection -Solved examples +Examples \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -23094,7 +22608,7 @@ noprefix "false" \end_layout \begin_layout Standard -Use known filterable constructions to show that +Use known filterable constructions to show that: \begin_inset Formula \[ F^{A}\triangleq\text{Int}\times\text{String}\rightarrow\bbnum 1+\text{Int}\times A+A\times\left(\bbnum 1+A\right)+(\text{Int}\rightarrow\bbnum 1+A+A\times A\times\text{String}) @@ -23120,7 +22634,7 @@ We need to analyze the structure of the functor \begin_inset Formula $F^{A}$ \end_inset -, +: \begin_inset Formula \begin{align*} R_{1}^{A}\triangleq\text{Int}\times\text{String}\rightarrow A\quad, & \quad\quad R_{2}^{A}\triangleq\text{Int}\rightarrow A\quad,\\ @@ -23133,7 +22647,7 @@ Now we can rewrite the type \begin_inset Formula $F^{A}$ \end_inset - as + as: \begin_inset Formula \[ F^{A}=R_{1}^{L^{A}}\quad,\quad\quad L^{A}\triangleq G^{A}+R_{2}^{H^{A}}\quad. @@ -23596,7 +23110,7 @@ inflate \end_inset - can be equivalently written as + can be equivalently written as: \begin_inset Formula \[ \text{inflate}_{F}=(x^{:A}\rightarrow\bbnum 0+x)^{\uparrow F}=\text{pu}_{\text{Opt}}^{\uparrow F}=\big(\psi_{(\_\rightarrow\text{true})}\big)^{\uparrow F}\quad. @@ -23618,7 +23132,7 @@ noprefix "false" \end_inset -) to derive +) to derive: \begin_inset Formula \[ \text{filt}_{F}(\_\rightarrow\text{true})=\psi_{(\_\rightarrow\text{true})}^{\uparrow F}\bef\text{deflate}_{F}=\text{inflate}_{F}\bef\text{deflate}_{F}\quad. @@ -23703,7 +23217,7 @@ deflate \end_inset - as + as: \begin_inset listings inline false status open @@ -23741,7 +23255,7 @@ The composition \begin_inset Formula $\text{deflate}_{\text{Opt}}\bef\text{inflate}_{\text{Opt}}$ \end_inset - is computed as + is computed as: \begin_inset Formula \begin{align*} & \text{deflate}_{\text{Opt}}\bef\text{inflate}_{\text{Opt}}=\,\begin{array}{|c||cc|} @@ -23811,7 +23325,7 @@ Some(None) \end_inset -, so the calculation corresponds to +, so the calculation corresponds to: \begin_inset listings inline false status open @@ -23960,7 +23474,7 @@ Any function lifted to \end_inset . - So + So: \begin_inset Formula \[ (1+\bbnum 0^{:K^{A}})\triangleright f^{\uparrow H}=1+\bbnum 0^{:K^{B}}\quad, @@ -23977,7 +23491,7 @@ regardless of the choice of \begin_inset Formula $f\triangleq\psi_{p}$ \end_inset -, we obtain +, we obtain: \begin_inset Formula \begin{equation} (1+\bbnum 0^{:K^{A}})\triangleright\psi_{p}^{\uparrow H}=1+\bbnum 0^{:K^{\bbnum 1+A}}\quad.\label{eq:emptyable-wrapper-psi-p-filter-derivation1} @@ -23985,7 +23499,7 @@ regardless of the choice of \end_inset -It remains to show that +It remains to show that: \begin_inset Formula \begin{equation} (1+\bbnum 0^{:K^{\bbnum 1+A}})\triangleright\text{deflate}_{H}\overset{?}{=}1+\bbnum 0^{:K^{A}}\quad.\label{eq:emptyable-wrapper-deflate-derivation1} @@ -24054,7 +23568,7 @@ true \end_inset - and get + and get: \begin_inset Formula \begin{equation} (1+\bbnum 0^{:K^{A}})\triangleright\psi_{(\_\rightarrow\text{true})}^{\uparrow H}=(1+\bbnum 0^{:K^{A}})\triangleright\text{inflate}_{H}=1+\bbnum 0^{:K^{\bbnum 1+A}}\quad.\label{eq:emptyable-wrapper-inflate-derivation1} @@ -24076,7 +23590,7 @@ noprefix "false" \end_inset -), we find +), we find: \begin_inset Formula \begin{align*} \text{expect to equal }1+\bbnum 0^{:K^{A}}:\quad & (\gunderline{1+\bbnum 0^{:K^{\bbnum 1+A}}})\triangleright\text{deflate}_{H}\\ @@ -24119,7 +23633,7 @@ noprefix "false" \begin_inset Formula $\text{liftOpt}_{H}$ \end_inset - as + as: \begin_inset Formula \begin{equation} (1+\bbnum 0^{:K^{B}})\triangleright\text{liftOpt}_{H}(f^{:A\rightarrow\bbnum 1+B})=1+\bbnum 0^{:K^{B}}\quad.\label{eq:empty-filter-remains-empty-via-liftOpt} @@ -24197,7 +23711,7 @@ We need to define \begin_inset Formula $\text{liftOpt}_{H}$ \end_inset -, + with types: \begin_inset Formula \[ \text{liftOpt}_{G}(f^{:A\rightarrow\bbnum 1+B}):G^{A}\rightarrow G^{B}\quad,\quad\quad\text{liftOpt}_{H}(f^{:A\rightarrow\bbnum 1+B}):\bbnum 1+K^{A}\rightarrow\bbnum 1+K^{B}\quad, @@ -24206,7 +23720,7 @@ We need to define \end_inset are already available and obey the same laws. - We need to implement the type signature + We need to implement the type signature: \begin_inset Formula \[ \text{liftOpt}_{F}(f^{:A\rightarrow\bbnum 1+B}):G^{K^{A}}\rightarrow G^{K^{B}}\quad. @@ -24435,7 +23949,7 @@ noprefix "false" \end_inset -), +): \begin_inset Formula \[ p^{:A\rightarrow\bbnum 1+B}\diamond_{_{\text{Opt}}}(\text{pu}_{\text{Opt}}^{:B\rightarrow\bbnum 1+B}\bef q^{:\bbnum 1+B\rightarrow\bbnum 1+C})\neq p\bef q\quad. @@ -24748,7 +24262,7 @@ unrolled \begin_inset Formula $n$ \end_inset - times as + times as: \begin_inset Formula \begin{align*} F^{A} & \triangleq P^{A}+Q^{A}\times F^{A}\cong P^{A}+Q^{A}\times(P^{A}+Q^{A}\times(P^{A}+...(P^{A}+Q^{A}\times F^{A})))\\ @@ -24839,7 +24353,7 @@ noprefix "false" \begin_inset Formula $G^{A}\triangleq\bbnum 1+A$ \end_inset -, that +, that: \begin_inset Formula \[ H^{A}\triangleq\bbnum 1+A\times\left(\bbnum 1+A\right)=\bbnum 1+Q^{A} @@ -24911,7 +24425,7 @@ Try to implement the function \begin_inset Formula $\text{inflate}_{C}:C^{A}\rightarrow C^{\bbnum 1+A}$ \end_inset -, + and write: \begin_inset Formula \[ \text{inflate}_{C}:(A\rightarrow Z)\rightarrow\bbnum 1+A\rightarrow Z\quad,\quad\quad\text{inflate}_{C}=c^{:A\rightarrow Z}\rightarrow p^{:\bbnum 1+A}\rightarrow\text{???}^{:Z}\quad. @@ -25796,7 +25310,7 @@ noprefix "false" \end_inset , or else we could make no progress with the calculations. - The required interchange was possible by using the law + The required interchange was possible due to the law \begin_inset space ~ \end_inset @@ -25810,7 +25324,7 @@ noprefix "false" \end_inset -), +): \begin_inset Formula \[ \text{filt}_{C}(p)\bef f^{\downarrow C}=f^{\downarrow C}\bef\text{filt}_{C}(f\bef p)\quad. @@ -25925,7 +25439,7 @@ Naturality law \begin_layout Plain Layout \size small -natural transformation +2-transformation \end_layout \end_inset @@ -25966,7 +25480,7 @@ natural transformation \begin_layout Plain Layout \size small -parameterized transformation +3-transformation \end_layout \end_inset @@ -26007,11 +25521,10 @@ parameterized transformation \begin_layout Plain Layout \size small -generalized lifting; \begin_inset Formula $A$ \end_inset --naturality +-lifting \end_layout \end_inset @@ -26052,11 +25565,10 @@ generalized lifting; \begin_layout Plain Layout \size small -generalized lifting; \begin_inset Formula $B$ \end_inset --naturality +-lifting \end_layout \end_inset @@ -26102,7 +25614,14 @@ Let us now look at each of these patterns in detail. \end_layout \begin_layout Paragraph -Natural transformations +\begin_inset Quotes eld +\end_inset + +2-transformation +\begin_inset Quotes erd +\end_inset + + \end_layout \begin_layout Standard @@ -26192,7 +25711,7 @@ List \end_inset -, +: \begin_inset Formula \[ \text{headOpt}:\text{List}^{A}\rightarrow\text{Opt}^{A}\quad. @@ -26297,34 +25816,35 @@ List \end_inset functor); the results will be equal. - We write this requirement as an equation called the naturality law of -\begin_inset listings -inline true + We write this requirement as an equation called the +\series bold +naturality law +\series default + +\begin_inset Index idx status open \begin_layout Plain Layout - +naturality law!of +\family typewriter headOption \end_layout \end_inset -: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "25col%" + of +\begin_inset listings +inline true status open \begin_layout Plain Layout -\begin_inset VSpace -210baselineskip% -\end_inset +headOption +\end_layout + +\end_inset +: \begin_inset Formula \[ \xymatrix{\text{List}^{A}\ar[r]\sp(0.55){\text{headOpt}^{A}}\ar[d]\sp(0.4){(f^{:A\rightarrow B})^{\uparrow\text{List}}} & \text{Opt}^{A}\ar[d]\sb(0.4){f^{\uparrow\text{Opt}}}\\ @@ -26335,29 +25855,6 @@ status open \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset Formula \[ (f^{:A\rightarrow B})^{\uparrow\text{List}}\bef\text{headOpt}^{B}=\text{headOpt}^{A}\bef f^{\uparrow\text{Opt}}\quad. @@ -26485,21 +25982,6 @@ natural transformation \end_inset as the following law: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "20col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -220baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{F^{A}\ar[r]\sp(0.55){t^{A}}\ar[d]\sp(0.4){(f^{:A\rightarrow B})^{\uparrow F}} & G^{A}\ar[d]\sp(0.4){f^{\uparrow G}}\\ @@ -26510,29 +25992,6 @@ status open \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -30baselineskip% -\end_inset - - \begin_inset Formula \begin{equation} (f^{:A\rightarrow B})^{\uparrow F}\bef t^{:F^{B}\rightarrow G^{B}}=t^{:F^{A}\rightarrow G^{A}}\bef f^{\uparrow G}\quad.\label{eq:law-natural-transformation-of-functors} @@ -26601,21 +26060,6 @@ The analogous naturality law for natural transformations \end_inset has exactly the same form, but the order of type parameters must be swapped: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "20col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -220baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{C^{A}\ar[r]\sp(0.55){t^{A}}\ar[d]\sp(0.4){(f^{:B\rightarrow A})^{\downarrow C}} & D^{A}\ar[d]\sp(0.4){f^{\downarrow D}}\\ @@ -26626,29 +26070,6 @@ status open \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -20baselineskip% -\end_inset - - \begin_inset Formula \begin{equation} (f^{:B\rightarrow A})^{\downarrow C}\bef t^{:C^{B}\rightarrow D^{B}}=t^{:C^{A}\rightarrow D^{A}}\bef f^{\downarrow D}\quad.\label{eq:law-natural-transformation-of-contrafunctors} @@ -26700,15 +26121,22 @@ noprefix "false" \end_layout \begin_layout Paragraph -Parameterized transformations +\begin_inset Quotes eld +\end_inset + +3-transformation +\begin_inset Quotes erd +\end_inset + + \end_layout \begin_layout Standard -The second pattern, which may be called +The second pattern, which we called \begin_inset Quotes eld \end_inset -parameterized transformation +3-transformation \begin_inset Quotes erd \end_inset @@ -26750,7 +26178,7 @@ parameterized transformation \end_inset are contrafunctors). - Examples of parameterized transformations are the methods + Examples of such type signatures are the methods \begin_inset listings inline true status open @@ -26834,8 +26262,7 @@ For functions of this kind, naturality laws must modify the argument of \end_inset when changing the order of lifted functions. - In order to formulate the naturality law for a parameterized transformation - + In order to formulate the naturality law for \begin_inset Formula $t^{A}:C^{A}\rightarrow F^{A}\rightarrow G^{A}$ \end_inset @@ -26909,7 +26336,7 @@ The argument of \end_inset . - So, the law is + So, the law is: \begin_inset Formula \begin{equation} f^{\uparrow F}\bef t(c)=t(c\triangleright f^{\downarrow C})\bef f^{\uparrow G}\quad.\label{eq:naturality-law-general-parameterized-transformation} @@ -26929,7 +26356,7 @@ A similar law can be derived for the case when \end_layout \begin_layout Standard -Parameterized transformations +Functions \begin_inset Formula $t:C^{A}\rightarrow F^{A}\rightarrow G^{A}$ \end_inset @@ -26960,7 +26387,7 @@ Parameterized transformations \begin_inset Formula $\tilde{t}$ \end_inset - as + as: \begin_inset Formula \[ f^{\uparrow F}\bef\tilde{t}=\tilde{t}\bef f^{\uparrow H}\quad,\quad\text{where}\quad\tilde{t}\triangleq p^{:F^{A}}\rightarrow c^{:C^{A}}\rightarrow p\triangleright t(c)\quad. @@ -27075,7 +26502,14 @@ Reduction to natural transformations works similarly when \end_layout \begin_layout Paragraph +\begin_inset Quotes eld +\end_inset + Liftings +\begin_inset Quotes erd +\end_inset + + \end_layout \begin_layout Standard @@ -27182,7 +26616,7 @@ generalized lifting \begin_inset Quotes erd \end_inset -, +: \begin_inset Formula \[ \text{lift}_{G,F}^{A,B}:(A\rightarrow G^{B})\rightarrow F^{A}\rightarrow F^{B}\quad, @@ -28047,7 +27481,7 @@ generalized lifting \begin_inset Formula $M$ \end_inset -, +: \begin_inset Formula \begin{align*} & \text{lift}_{M,F}:(A\rightarrow M^{B})\rightarrow F^{A}\rightarrow F^{B}\quad,\\ @@ -28057,7 +27491,7 @@ generalized lifting \end_inset and postulating the required properties as the set of identity, associativity, - and composition laws, + and composition laws: \begin_inset Formula \begin{align*} & \text{lift}_{M,F}(\text{pu}_{M}^{:A\rightarrow M^{A}})=\text{id}^{:F^{A}\rightarrow F^{A}}\quad,\quad\quad\text{lift}_{M,F}(f)\bef\text{lift}_{M,F}(g)=\text{lift}_{M,F}(f\diamond_{_{M}}g)\quad,\\ @@ -28342,13 +27776,12 @@ category \begin_layout Standard \align center \begin_inset Tabular - + - \begin_inset Text @@ -28367,22 +27800,6 @@ Category \begin_layout Plain Layout -\series bold -\size small -Types -\begin_inset Formula $A,B,...$ -\end_inset - - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - \series bold \size small Morphisms @@ -28437,20 +27854,6 @@ plain \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $\text{Int},\text{String},...$ -\end_inset - - \end_layout \end_inset @@ -28513,20 +27916,6 @@ reversed \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $\text{Int},\text{String},...$ -\end_inset - - \end_layout \end_inset @@ -28593,20 +27982,6 @@ reversed \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $\text{Int},\text{String},...$ -\end_inset - - \end_layout \end_inset @@ -28673,20 +28048,6 @@ reversed \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $\text{Int},\text{String},...$ -\end_inset - - \end_layout \end_inset @@ -28753,20 +28114,6 @@ reversed \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $F^{\text{Int}},F^{\text{String}},...$ -\end_inset - - \end_layout \end_inset diff --git a/sofp-src/sofp-filterable.tex b/sofp-src/sofp-filterable.tex index c82886627..1cfcb2f8a 100644 --- a/sofp-src/sofp-filterable.tex +++ b/sofp-src/sofp-filterable.tex @@ -18,7 +18,7 @@ \section{Practical uses of filtering\label{sec:Practical-uses-of-filterable-func The Scala standard library defines the \lstinline!filter! method on sequences, sets, and other data structures. An example of using -\lstinline!filter! is the following calculation, +\lstinline!filter! is the following calculation: \[ \sum_{x\in\mathbb{Z};\,0\leq x\leq100;\,\cos x>0}\sqrt{\cos\left(x\right)}\approx38.71\quad. \] @@ -85,7 +85,7 @@ \section{Practical uses of filtering\label{sec:Practical-uses-of-filterable-func a method called \lstinline!withFilter! with the same type signature as the \lstinline!filter! method. The type signatures of \lstinline!map! and \lstinline!withFilter! methods for a type constructor \lstinline!F[_]! -can be written as +can be written as: \begin{lstlisting} class F[A] { // F[A] is some type constructor that has .map and .withFilter methods. def map[B](f: A => B): F[B] = ... @@ -127,7 +127,7 @@ \subsection{Examples and intuitions for the filtering operation} Note that \lstinline!Option[T]! is written in the type notation as a disjunctive type $\bbnum 1+T$, while \lstinline!List[T]! can be -viewed as an \textsf{``}infinite disjunction\textsf{''}, +viewed as an \textsf{``}infinite disjunction\textsf{''}: \begin{equation} \text{List}^{T}=1+T+T\times T+T\times T\times T+...\label{eq:list-infinite-disjunction} \end{equation} @@ -140,7 +140,7 @@ \subsection{Examples and intuitions for the filtering operation} type supporting a different number of values of $T$, including \emph{zero} values. When the filtering operation \lstinline!.filter(p)! is applied, some values of type $T$ will fail the predicate \lstinline!p! and -will be removed from the collection. The example +will be removed from the collection. This example: \begin{lstlisting} scala> List(64, 128).filter(_ > 100) res0: List[Int] = List(128) @@ -242,7 +242,7 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva y = f(x) if p(y) } yield y - // Translating the functor block into methods: + // Rewritten via method chains: val result1 = xs.map(f).filter(p) \end{lstlisting} % @@ -254,7 +254,7 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva if p(f(x)) y = f(x) } yield y - // Translating the functor block into methods: + // Rewritten via method chains: val result2 = xs.filter(x => p(f(x))).map(f) \end{lstlisting} % @@ -284,7 +284,7 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva if p1(x) if p2(x) } yield x - // Translating the functor block into methods: + // Rewritten via method chains: val result1 = xs.filter(p1).filter(p2) \end{lstlisting} % @@ -296,7 +296,7 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva if (p1(x) && p2(x)) } yield x - // Translating the functor block into methods: + // Rewritten via method chains: val result2 = xs.filter(x => p1(x) && p2(x)) \end{lstlisting} % @@ -305,11 +305,11 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva \vspace{0\baselineskip} When a filter predicate \lstinline!p(x)! returns \lstinline!true! -for all \lstinline!x!, the filtering operation \lstinline!xs.filter(p)! -will never discard any values. So we expect the result to remain the -same if we \emph{delete} the line \textsf{``}\lstinline!if true!\textsf{''} from +for all \lstinline!x!, the filtering call \lstinline!xs.filter(p)! +will never discard any values. So, we expect the result to remain +the same if we \emph{delete} the line \textsf{``}\lstinline!if true!\textsf{''} from a functor block program. The corresponding code equivalence can be -written as +written as: \begin{lstlisting} xs.filter(_ => true) == xs \end{lstlisting} @@ -320,7 +320,7 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva \textsf{``}\lstinline!if p(x)!\textsf{''}. In particular, we should be able to use a partial function safely as long as that function is defined for \lstinline!x! such that \lstinline!p(x) == true!. To express this -in code, first define a general \textsf{``}factory\textsf{''} for partial functions, +in code, first define a general \textsf{``}factory\textsf{''} for partial functions: \begin{lstlisting} def if_p[A, B](p: A => Boolean)(f: A => B): A => B = x => p(x) match { case true => f(x) } \end{lstlisting} @@ -331,7 +331,7 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva brevity by $f_{|p}$. Since the \lstinline!Boolean! type is equivalent to a disjunction of two \textsf{``}named unit types\textsf{''}, $\bbnum 2\cong\bbnum 1+\bbnum 1$ (meaning \textsf{``}\lstinline!false!\textsf{''} + \textsf{``}\lstinline!true!\textsf{''}), we can -write the code notation for the function $f_{|p}$ as +write the code notation for the function $f_{|p}$ as: \begin{equation} (f^{:A\rightarrow B})_{|p}\triangleq x^{:A}\rightarrow p(x)\triangleright\,\begin{array}{|c||c|} & B\\ @@ -357,7 +357,7 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva if p(x) y = f(x) } yield y - // Translating the functor block into methods: + // Rewritten via method chains: val result1 = xs.filter(p).map(f) \end{lstlisting} % @@ -367,9 +367,9 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva val result2 = for { x <- xs if p(x) - y = fp(x) // Defined as val fp = if_p(p)(f) + y = fp(x) // Here fp = if_p(p)(f) } yield y - // Translating the functor block into methods: + // Rewritten via method chains: val result2 = xs.filter(p).map(fp) \end{lstlisting} % @@ -382,26 +382,27 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva \textsf{``}laws\textsf{''} (i.e., equations) that any reasonable \lstinline!filter! must satisfy. In the code notation, \lstinline!filter! is $\text{filt}_{F}$: \[ -\text{filt}_{F}:(A\rightarrow\bbnum 2)\rightarrow F^{A}\rightarrow F^{A}\quad, +\text{filt}_{F}:(A\rightarrow\bbnum 2)\rightarrow F^{A}\rightarrow F^{A}\quad. \] -and its $4$ laws (called the naturality, identity, composition, and -partial function laws of \lstinline!filter!) are:\index{composition law!of filter@of \texttt{filter}}\index{naturality law!of filter@of \texttt{filter}}\index{identity laws!of filter@of \texttt{filter}}\index{partial function law!of filter@of \texttt{filter}} +The $4$ laws (called the naturality, identity, composition, and partial +function laws) are formulated for arbitrary functions $f^{:A\rightarrow B}$, +$p^{:A\rightarrow\bbnum 2}$, $p_{1}^{:A\rightarrow\bbnum 2}$, $p_{2}^{:A\rightarrow\bbnum 2}$, +and $q^{:B\rightarrow\bbnum 2}$:\index{composition law!of filter@of \texttt{filter}}\index{naturality law!of filter@of \texttt{filter}}\index{identity laws!of filter@of \texttt{filter}}\index{partial function law!of filter@of \texttt{filter}} \begin{align} -{\color{greenunder}\text{naturality law}:}\quad & f^{\uparrow F}\bef\text{filt}_{F}(p)=\text{filt}_{F}(f\bef p)\bef f^{\uparrow F}\quad\text{for }\forall(f^{:A\rightarrow B},~p^{:B\rightarrow\bbnum 2})\quad.\label{eq:naturality-law-of-filter}\\ +{\color{greenunder}\text{naturality law}:}\quad & f^{\uparrow F}\bef\text{filt}_{F}(q)=\text{filt}_{F}(f\bef q)\bef f^{\uparrow F}\quad.\label{eq:naturality-law-of-filter}\\ {\color{greenunder}\text{identity law}:}\quad & \text{filt}_{F}(\_\rightarrow\text{true})=\text{id}^{:F^{A}\rightarrow F^{A}}\quad.\label{eq:identity-law-of-filter}\\ -{\color{greenunder}\text{composition law}:}\quad & \text{filt}_{F}(p_{1})\bef\text{filt}_{F}(p_{2})=\text{filt}_{F}(x\rightarrow p_{1}(x)\wedge p_{2}(x))\quad\text{for }\forall(p_{1}^{:A\rightarrow\bbnum 2},~p_{2}^{:A\rightarrow\bbnum 2})\quad.\label{eq:composition-law-of-filter}\\ -{\color{greenunder}\text{partial function law}:}\quad & \text{filt}_{F}(p)\bef f^{\uparrow F}=\text{filt}_{F}(p)\bef f_{|p}^{\uparrow F}\quad\text{for }\forall(f^{:A\rightarrow B},~p^{:A\rightarrow\bbnum 2})\quad.\label{eq:partial-function-law-of-filter} +{\color{greenunder}\text{composition law}:}\quad & \text{filt}_{F}(p_{1})\bef\text{filt}_{F}(p_{2})=\text{filt}_{F}(x\rightarrow p_{1}(x)\wedge p_{2}(x))\quad.\label{eq:composition-law-of-filter}\\ +{\color{greenunder}\text{partial function law}:}\quad & \text{filt}_{F}(p)\bef f^{\uparrow F}=\text{filt}_{F}(p)\bef f_{|p}^{\uparrow F}\quad.\label{eq:partial-function-law-of-filter} \end{align} -The following type diagram illustrates the naturality law of \lstinline!filter!:\vspace{-0.5\baselineskip} +The following type diagram illustrates the naturality law of \lstinline!filter!: \[ -\xymatrix{\xyScaleY{1.4pc}\xyScaleX{7.0pc}F^{A}\ar[r]\sp(0.5){\text{filt}_{F}(f^{:A\rightarrow B}\bef p^{:B\rightarrow\bbnum 2})}\ar[d]\sb(0.45){(f^{:A\rightarrow B})^{\uparrow F}} & F^{A}\ar[d]\sp(0.45){(f^{:A\rightarrow B})^{\uparrow F}}\\ -F^{B}\ar[r]\sp(0.5){\text{filt}_{F}(p^{:B\rightarrow\bbnum 2})} & F^{B} +\xymatrix{\xyScaleY{1.4pc}\xyScaleX{7.0pc}F^{A}\ar[r]\sp(0.5){\text{filt}_{F}(f^{:A\rightarrow B}\bef q^{:B\rightarrow\bbnum 2})}\ar[d]\sb(0.45){(f^{:A\rightarrow B})^{\uparrow F}} & F^{A}\ar[d]\sp(0.45){(f^{:A\rightarrow B})^{\uparrow F}}\\ +F^{B}\ar[r]\sp(0.5){\text{filt}_{F}(q^{:B\rightarrow\bbnum 2})} & F^{B} } \] -\vspace{-0.5\baselineskip} -A functor $F$ is called \textbf{filterable} if there exists a function -$\text{filt}_{F}$ satisfying these four laws.\index{filterable!functor} +A functor $F$ is called \textbf{filterable} if there is a function +$\text{filt}_{F}$ satisfying these laws.\index{filterable!functor} We may define a typeclass \lstinline!Filterable! and extension methods \lstinline!filter! and \lstinline!withFilter! like this: @@ -428,15 +429,15 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva def checkFilteringLaws[F[_] : Filterable : Functor, A, B](implicit faEv: Arbitrary[F[A]], fbEv: Arbitrary[F[B]], abEv: Arbitrary[A => B], aEv: Arbitrary[A => Boolean], bEv: Arbitrary[B => Boolean]): Assertion = { - forAll { (f: A => B, p: B => Boolean, fa: F[A]) => // Naturality law. + forAll { (f: A => B, p: B => Boolean, fa: F[A]) => // Naturality law. fa.map(f).filter(p) shouldEqual fa.filter(f andThen p).map(f) } - forAll { (p1: B => Boolean, p2: B => Boolean, fa: F[B]) => // Composition law. + forAll { (p1: B => Boolean, p2: B => Boolean, fa: F[B]) => // Composition law. fa.filter(p1).filter(p2) shouldEqual fa.filter(b => p1(b) && p2(b)) } - forAll { (fb: F[B]) => fb.filter(_ => true) shouldEqual fb } // Identity law. + forAll { (fb: F[B]) => fb.filter(_ => true) shouldEqual fb } // Identity law. - forAll { (f: A => B, p: A => Boolean, fa: F[A]) => // Partial function law. + forAll { (f: A => B, p: A => Boolean, fa: F[A]) => // Partial function law. fa.filter(p).map(f) shouldEqual fa.filter(p).map[B](x => p(x) match { case true => f(x) }) } } @@ -447,7 +448,7 @@ \subsection{The laws of filtering: Motivation and derivation\label{subsec:Motiva implicit val filterableOrders = new Filterable[Orders] { def filt[A](p: A => Boolean)(fa: F[A]): F[A] = fa.filter(p) } -checkFilteringLaws[Orders, Int, String] // Need to set type parameters to specific types. +checkFilteringLaws[Orders, Int, String] // Need to set type parameters. \end{lstlisting} @@ -490,9 +491,9 @@ \subsection{Examples of non-filterable functors\label{subsec:Examples-of-non-fil blocks: \begin{lstlisting} scala> for { x <- Orders(Some(500), Some(2000)) - if x < 1000 // Intuition says that values of x must be below 1000 from now on. - y = s"Amount: $x" // So, the value x = 2000 should never appear in this line. -} yield y // But the final result does not correspond to this intuition: + if x < 1000 // Intuition says that values of x must be below 1000 from now on. + y = s"Amount: $x" // So, the value x = 2000 should never appear in this line. +} yield y // But the final result does not correspond to this intuition: res2: Orders[String] = Orders(Some("Amount: 500"), Some("Amount: 2000")) \end{lstlisting} This computation violates the partial function law because the value @@ -564,7 +565,7 @@ \subsection{Examples of non-filterable functors\label{subsec:Examples-of-non-fil defined values. For this reason, it is allowed to use the \lstinline!Boolean! type in fully parametric functions. -\subsection{Solved examples: Programming with filterable functors\index{solved examples} } +\subsection{Examples: Programming with filterable functors\index{examples (with code)} } \subsubsection{Example \label{subsec:filt-solved-example-1}\ref{subsec:filt-solved-example-1}} @@ -581,12 +582,6 @@ \subsubsection{Example \label{subsec:filt-solved-example-1}\ref{subsec:filt-solv data type is the functor $F^{A}\triangleq\bbnum 1+A\times A$. This functor is filterable if we can implement a lawful \lstinline!filter! function. Begin writing code for \lstinline!filter!: - -\begin{wrapfigure}{l}{0.49\columnwidth}% -\vspace{0.05\baselineskip} -\begin{comment} --75baselineskip\% if inside the page, 25 if at page start -\end{comment} \begin{lstlisting}[numbers=left] type F[A] = Option[(A, A)] def filter[A](p: A => Boolean): F[A] => F[A] = { @@ -594,14 +589,10 @@ \subsubsection{Example \label{subsec:filt-solved-example-1}\ref{subsec:filt-solv case Some((a1, a2)) => ??? } \end{lstlisting} - -\vspace{-1.25\baselineskip} -\end{wrapfigure}% - -\noindent In line~4, we need to compute a value of type $F^{A}$ -using the given values \lstinline!a1! and \lstinline!a2!. We need -to check whether the predicate \lstinline!p! holds for \lstinline!a1! -and \lstinline!a2!. What if \lstinline!p(a1) == false! but \lstinline!p(a2) == true!? +In line~4, we need to compute a value of type $F^{A}$ using the +given values \lstinline!a1! and \lstinline!a2!. We need to check +whether the predicate \lstinline!p! holds for \lstinline!a1! and +\lstinline!a2!. What if \lstinline!p(a1) == false! but \lstinline!p(a2) == true!? We need to remove \lstinline!a1! from the result, or else the filtering laws will not hold. But the functor $F^{A}\triangleq\bbnum 1+A\times A$ does not allow us to keep just one of the values of type $A$; it @@ -716,12 +707,15 @@ \subsubsection{Example \label{subsec:filt-solved-example-1}\ref{subsec:filt-solv $s\triangleq\bbnum 0+a_{1}\times a_{2}$. The filtering operation $s\triangleright\text{filt}_{F}(p_{1})\triangleright\text{filt}_{F}(p_{2})$ will produce different results according to the values of the predicates -$p_{1}$ and $p_{2}$ applied to $a_{1}$ and $a_{2}$, which we can -summarize as a table: +$p_{1}$ and $p_{2}$ applied to $a_{1}$ and $a_{2}$. We can summarize +all results in a table, where we denote for brevity the left-hand +side of the composition law by $\text{L.H.S.}\triangleq s\triangleright\text{filt}_{F}(p_{1})\triangleright\text{filt}_{F}(p_{2})$ +and the right-hand side by $\text{R.H.S.}\triangleq s\triangleright\text{filt}_{F}(p_{12})$, +where $p_{12}\triangleq x\rightarrow p_{1}(x)\wedge p_{2}(x)$: \noindent \begin{center} \begin{tabular}{|c|c|c|c|c|c|c|c|c|} \hline -{\footnotesize{}$p_{1}(a_{1})$} & {\footnotesize{}$p_{1}(a_{2})$} & {\footnotesize{}$p_{2}(a_{1})$} & {\footnotesize{}$p_{2}(a_{2})$} & {\footnotesize{}$p_{12}(a_{1})$} & {\footnotesize{}$p_{12}(a_{2})$} & {\footnotesize{}$s\triangleright\text{filt}_{F}(p_{1})$} & {\footnotesize{}$s\triangleright\text{filt}_{F}(p_{1})\triangleright\text{filt}_{F}(p_{2})$} & {\footnotesize{}$s\triangleright\text{filt}_{F}(p_{12})$}\tabularnewline +{\footnotesize{}$p_{1}(a_{1})$} & {\footnotesize{}$p_{1}(a_{2})$} & {\footnotesize{}$p_{2}(a_{1})$} & {\footnotesize{}$p_{2}(a_{2})$} & {\footnotesize{}$p_{12}(a_{1})$} & {\footnotesize{}$p_{12}(a_{2})$} & {\footnotesize{}$s\triangleright\text{filt}_{F}(p_{1})$} & {\footnotesize{}$\text{L.H.S.}$} & {\footnotesize{}$\text{R.H.S.}$}\tabularnewline \hline \hline {\small{}}\lstinline!true! & {\small{}}\lstinline!true! & {\small{}}\lstinline!true! & {\small{}}\lstinline!false! & {\small{}}\lstinline!true! & {\small{}}\lstinline!false! & {\small{}$\bbnum 0+a_{1}\times a_{2}$} & {\small{}$\bbnum 0+a_{1}\times a_{1}$} & {\small{}$\bbnum 0+a_{1}\times a_{1}$}\tabularnewline @@ -737,24 +731,24 @@ \subsubsection{Example \label{subsec:filt-solved-example-1}\ref{subsec:filt-solv \end{tabular} \par\end{center} -Here we denoted $p_{12}\triangleq x\rightarrow p_{1}(x)\wedge p_{2}(x)$ -for brevity. We see that the last two columns are always equal, which -verifies the composition law. We omitted some rows from the table -because the filtering code is completely symmetric with respect to -interchanging $a_{1}$ and $a_{2}$, and because the composition law -is trivial when $p_{1}=p_{2}$. +We see that the last two columns are always equal, which verifies +the composition law. We omitted some rows from the table because the +filtering code is completely symmetric with respect to interchanging +$a_{1}$ and $a_{2}$, and because the composition law is trivial +when $p_{1}=p_{2}$. The partial function law holds because the code of \lstinline!filter! will always remove the value \lstinline!a1!, or \lstinline!a2!, or both of them when the filtering predicate \lstinline!p! returns \lstinline!false! for any of those values. -So, we have seen that all filtering laws hold. If the program\textsf{'}s requirements -change, the \lstinline!filter! function will need to be implemented -differently. For instance, the first server might be the only source -of credentials: the second server may copy the first server\textsf{'}s credentials -if needed, but the cluster will go down whenever the first server\textsf{'}s -credentials expire. This corresponds to the code: +So, we have proved that all filtering laws hold for the \lstinline!filter! +function shown above. If the program\textsf{'}s requirements change, the \lstinline!filter! +function will need to be changed. For instance, suppose the first +server is now the only source of credentials. The second server may +copy the first server\textsf{'}s credentials if needed, but the cluster will +go down whenever the first server\textsf{'}s credentials expire. This corresponds +to the code: \begin{lstlisting} def filter[A](p: A => Boolean): F[A] => F[A] = { @@ -762,7 +756,7 @@ \subsubsection{Example \label{subsec:filt-solved-example-1}\ref{subsec:filt-solv case Some((a1, a2)) => (p(a1), p(a2)) match { case (true, true) => Some((a1, a2)) case (true, false) => Some((a1, a1)) - case (false, _) => None // The cluster is down if credentials expired for server 1. + case (false, _) => None // The cluster is down if credentials expire for server 1. } } \end{lstlisting} @@ -772,8 +766,8 @@ \subsubsection{Example \label{subsec:filt-solved-example-1}\ref{subsec:filt-solv def filter[A](p: A => Boolean): F[A] => F[A] = { case None => None case Some((a1, a2)) => (p(a1), p(a2)) match { - case (true, true) => Some((a1, a2)) // Both credentials are valid. - case _ => None // The cluster is down if any credentials expired. + case (true, true) => Some((a1, a2)) // Both credentials are valid. + case _ => None // The cluster is down when any of the credentials expire. } } \end{lstlisting} @@ -794,7 +788,7 @@ \subsubsection{Example \label{subsec:filt-solved-example-2}\ref{subsec:filt-solv as $\bbnum 1+A+A\times A+A\times A\times A$ and can be implemented in Scala by: \begin{lstlisting}[mathescape=true] -sealed trait JohnsCoupons[A] // This represents the type $\color{dkgreen}\bbnum 1+A+A\times A+A\times A\times A$. +sealed trait JohnsCoupons[A] // This represents the type $\color{dkgreen}\bbnum 1+A+A\times A+A\times A\times A$. final case class John0[A]() extends JohnsCoupons[A] final case class John1[A](c1: A) extends JohnsCoupons[A] final case class John2[A](c1: A, c2: A) extends JohnsCoupons[A] @@ -821,7 +815,7 @@ \subsubsection{Example \label{subsec:filt-solved-example-2}\ref{subsec:filt-solv case John3(c1, c2, c3) => if (p(c1) && p(c2) && p(c3)) John3(c1, c2, c3) else John0() } def filterJill[A](p: A => Boolean): JillsCoupons[A] => JillsCoupons[A] = { - case Jill0() => Jill0() // We must remove each invalid coupon but keep the rest. + case Jill0() => Jill0() // We must remove all the invalid coupons. case Jill1(c1) => if (p(c1)) Jill1(c1) else Jill0() case Jill2(c1, c2) => (p(c1), p(c2)) match { case (true, true) => Jill2(c1, c2) @@ -912,9 +906,12 @@ \subsubsection{Example \label{subsec:filt-solved-example-4}\ref{subsec:filt-solv (see Statement~\ref{subsec:Statement-filterable-functor-product} below). -\textbf{(b)} The functor $F$ is equivalent to $F^{A}\cong\text{Int}\times\left(\bbnum 1+A+A\times A+A\times A\times A\right)=\text{Int}\times\text{JohnsCoupons}^{A}$, +\textbf{(b)} The functor $F$ can be written equivalently as: +\[ +F^{A}\cong\text{Int}\times\left(\bbnum 1+A+A\times A+A\times A\times A\right)=\text{Int}\times\text{JohnsCoupons}^{A}\quad, +\] where we used the functor \lstinline!JohnsCoupons! from Example~\ref{subsec:filt-solved-example-2}. -So we use the same filtering operation for \lstinline!JohnsCoupons! +So, we use the same filtering operation for \lstinline!JohnsCoupons! as in Example~\ref{subsec:filt-solved-example-2}, while keeping the \lstinline!Int! value unchanged: \begin{lstlisting} @@ -964,13 +961,13 @@ \subsubsection{Example \label{subsec:filt-solved-example-4}\ref{subsec:filt-solv $\text{Int}\times Z\times A\times A$ pass the filter, we will need to remove both of them and to return a value of type $Z$. Luckily, we have a value of type $Z$ within $\text{Int}\times Z\times A\times A$. -So we can implement \lstinline!filter! e.g., like this, +So we can implement \lstinline!filter! e.g., like this: \begin{lstlisting} -type F[A] = Either[Z, (Int, Z, A, A)] // The type `Z` must be already defined. +type F[A] = Either[Z, (Int, Z, A, A)] // The type `Z` must be already defined. def filter[A](p: A => Boolean): F[A] => F[A] = { case Left(z) => Left(z) case Right((n, z, a1, a2)) => if (p(a1) && p(a2)) Right((n, z, a1, a2)) - else Left(z) // If anything fails the filter, use `z`. + else Left(z) // If anything fails the filter condition, use the value `z`. } \end{lstlisting} The filtering laws will hold similarly to Example~\ref{subsec:filt-solved-example-1}. @@ -1088,40 +1085,24 @@ \subsection{Simplifying the filtering laws: Motivation for \texttt{deflate\label \end{lstlisting} We can always convert a value of type $F^{A}$ back into a value of type $F^{\bbnum 1+A}$ (as long as $F$ is a functor): - -\begin{wrapfigure}{l}{0.54\columnwidth}% -\vspace{-0.6\baselineskip} \begin{lstlisting} -def inflate[F[_]: Functor, A]: F[A] => F[Option[A]] = - _.map(x => Some(x)) +def inflate[F[_]: Functor, A]: F[A] => F[Option[A]] = _.map(x => Some(x)) \end{lstlisting} - -\vspace{-1.2\baselineskip} -\end{wrapfigure}% - -\noindent The code notation for the function \lstinline!inflate! -is:\vspace{-0.4\baselineskip} +The code notation for the function \lstinline!inflate! is: \[ \text{inflate}^{F,A}\triangleq(x^{:A}\rightarrow\bbnum 0^{:\bbnum 1}+x)^{\uparrow F}\quad. \] It remains to convert $F^{\bbnum 1+A}$ to $F^{A}$. If we could \emph{somehow} -do that, say, via a function we call \lstinline!deflate!: - -\begin{wrapfigure}{l}{0.54\columnwidth}% -\vspace{-0.85\baselineskip} +do that, say, via a function we will call \lstinline!deflate!: \begin{lstlisting} def deflate[F[_], A]: F[Option[A]] => F[A] = ??? \end{lstlisting} - -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -\noindent \vspace{-0.5\baselineskip} +or, in the code notation: \[ \text{deflate}^{F,A}:F^{\bbnum 1+A}\rightarrow F^{A}\quad, \] -we would then express \lstinline!filter! through \lstinline!map! -and \lstinline!deflate! like this:\vspace{-0.3\baselineskip} +we would then be able to express \lstinline!filter! through \lstinline!map! +and \lstinline!deflate! like this: \begin{align*} & \quad\quad\quad\quad\left(\text{filt}_{F}(p)\right)^{:F^{A}\rightarrow F^{A}}=\text{inflate}\bef\big(\text{filt}_{\text{Opt}}(p)\big)^{\uparrow F}\bef\text{deflate}\quad.\\ & \xymatrix{\xyScaleX{5pc}\xyScaleY{0.8pc}F^{A}\ar[r]\sp(0.45){\text{inflate}} & F^{\bbnum 1+A}\ar[r]\sp(0.55){\big(\text{filt}_{\text{Opt}}(p)\big)^{\uparrow F}} & F^{\bbnum 1+A}\ar[r]\sp(0.55){\text{deflate}} & F^{A}} @@ -1131,7 +1112,7 @@ \subsection{Simplifying the filtering laws: Motivation for \texttt{deflate\label We notice that both functions in the composition $\text{inflate}\bef(\text{filt}_{\text{Opt}}(p))^{\uparrow F}$ are some lifted functions in the functor $F$, and so we can simplify -that composition to a single lifted function:\vspace{-0.35\baselineskip} +that composition to a single lifted function: \begin{align*} & \gunderline{\text{inflate}}\bef(\text{filt}_{\text{Opt}}(p))^{\uparrow F}\\ {\color{greenunder}\text{definition of }\text{inflate}:}\quad & =(x^{:A}\rightarrow\bbnum 0^{:\bbnum 1}+x)^{\uparrow F}\bef(\text{filt}_{\text{Opt}}(p))^{\uparrow F}\\ @@ -1139,31 +1120,22 @@ \subsection{Simplifying the filtering laws: Motivation for \texttt{deflate\label \end{align*} We will need to use this function often, so let us call it $\psi(p)$ or even shorter, $\psi_{p}$, for convenience: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.6\baselineskip} \begin{lstlisting} def psi[A](p: A => Boolean): A => Option[A] = x => Some(x).filter(p) \end{lstlisting} - -\vspace{-1.25\baselineskip} -\end{wrapfigure}% - -\noindent \vspace{-1.4\baselineskip} +In the code notation: \begin{align*} & \psi^{A}:(A\rightarrow\bbnum 2)\rightarrow A\rightarrow\bbnum 1+A\quad,\\ & \psi_{p}\triangleq\psi(p^{:A\rightarrow\bbnum 2})\triangleq x^{:A}\rightarrow\text{filt}_{\text{Opt}}(p)(\bbnum 0+x)\quad. \end{align*} -\vspace{-1.5\baselineskip} Using the function $\psi$, we can express the \lstinline!filter! -operation as:\vspace{-1\baselineskip} +operation as: \begin{align} & \xymatrix{\xyScaleX{4pc}F^{A}\ar[r]\sp(0.5){\psi_{p}^{\uparrow F}} & F^{\bbnum 1+A}\ar[r]\sp(0.55){\text{deflate}} & F^{A}} \quad\quad\quad\quad\quad\text{filt}_{F}(p)=\psi_{p}^{\uparrow F}\bef\text{deflate}\quad.\label{eq:def-filter-through-deflate} \end{align} -\vspace{-1.5\baselineskip} \begin{lstlisting} def filter[A](p: A => Boolean)(fa: F[A]): F[A] = deflate(fa.map(psi(p))) \end{lstlisting} @@ -1175,21 +1147,12 @@ \subsection{Simplifying the filtering laws: Motivation for \texttt{deflate\label The Scala library has a \lstinline!flatten! method for \lstinline!Seq! with exactly that type signature; it removes all empty \lstinline!Option! values from a sequence. This shows how to derive the \lstinline!deflate! -method for any filterable $F$: use $F$\textsf{'}s \lstinline!filter! to -remove the empty \lstinline!Option! values from $F^{\bbnum 1+A}$. - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0.85\baselineskip} +method for any filterable $F$: just use $F$\textsf{'}s \lstinline!filter! +to remove the empty \lstinline!Option! values from $F^{\bbnum 1+A}$. \begin{lstlisting}[mathescape=true] -def deflate[F[_]: Filterable : Functor, A]: - F[Option[A]] => F[A] = +def deflate[F[_]: Filterable : Functor, A]: F[Option[A]] => F[A] = _.filter(_.nonEmpty).map(_.get) \end{lstlisting} - -\vspace{-1.25\baselineskip} -\end{wrapfigure}% - -\noindent \vspace{-1.8\baselineskip} \begin{align} & \text{deflate}:\xymatrix{\xyScaleX{4.5pc}F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{filt}_{F}(\text{nonEmpty)}} & F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{get}^{\uparrow F}} & F^{A}} \nonumber \\ @@ -1224,7 +1187,7 @@ \subsection{Simplifying the filtering laws: Motivation for \texttt{deflate\label \end{lstlisting} -\subsubsection{Example \label{subsec:Example-ff-deflate-1-1}\ref{subsec:Example-ff-deflate-1-1}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-ff-deflate-1-1}\ref{subsec:Example-ff-deflate-1-1}\index{examples (with code)}} Use \lstinline!deflate! to implement a \lstinline!Filterable! instance for the functor $F^{A}\triangleq Z\rightarrow\text{List}^{A}$. @@ -1233,7 +1196,7 @@ \subsubsection{Example \label{subsec:Example-ff-deflate-1-1}\ref{subsec:Example- The type signature of \lstinline!deflate! is implemented as: \begin{lstlisting} -type F[A] = Z => List[A] // The type Z should have been defined before. +type F[A] = Z => List[A] // The type Z must be defined before. def deflateF[A](fa: F[Option[A]]): F[A] = { z => fa(z).flatten } \end{lstlisting} We used Scala\textsf{'}s library method \lstinline!flatten! with the type @@ -1258,7 +1221,7 @@ \subsubsection{Example \label{subsec:Example-ff-deflate-1}\ref{subsec:Example-ff The type signature of \lstinline!deflate! is $F^{\bbnum 1+A}\rightarrow F^{A}$ and can be implemented, e.g., as: \begin{lstlisting} -type F[A] = Either[(A, A), Z => Z] // The type Z should have been defined before. +type F[A] = Either[(A, A), Z => Z] // The type Z must be defined before. def deflateF[A]: F[Option[A]] => F[A] = { // Pattern-match on Either[(Option[A], Option[A]), Z => Z]. case Left((Some(a1), Some(a2))) => Left((a1, a2)) // Both values pass the filter. case Left(_) => Right(identity) // We can use a fixed value of type Z => Z. @@ -1329,7 +1292,7 @@ \subsubsection{Statement \label{subsec:Statement-filter-to-deflate-equivalence}\ (i.e., $\bbnum 1+\bbnum 1$) instead of \lstinline!Boolean! (i.e., $\bbnum 2$): \begin{lstlisting}[mathescape=true] -def nonEmpty[A]: Option[A] => Option[Unit] = _.map(_ => ()) // Option[Unit] $\color{dkgreen}\cong$ Boolean +def nonEmpty[A]: Option[A] => Option[Unit] = _.map(_ => ()) // Option[Unit] $\color{dkgreen}\cong$ Boolean def get[A]: Option[A] => A = { case Some(a) => a } \end{lstlisting} \begin{align} @@ -1353,7 +1316,7 @@ \subsubsection{Statement \label{subsec:Statement-filter-to-deflate-equivalence}\ defined types such as \lstinline!Int! or \lstinline!String!. The function $\psi$ is also fully parametric since we can write it as: \begin{lstlisting}[mathescape=true] -def psi[A](p: A => Option[Unit]): A => Option[A] = x => p(x).map(_ => x) // Option[Unit] $\color{dkgreen}\cong$ Boolean +def psi[A](p: A => Option[Unit]): A => Option[A] = x => p(x).map(_ => x) // Option[Unit] $\color{dkgreen}\cong$ Boolean \end{lstlisting} \begin{align*} {\color{greenunder}\text{view }p^{:A\rightarrow\bbnum 2}(x^{:A})\text{ as having type }\bbnum 1+\bbnum 1:}\quad & x^{:A}\triangleright\psi_{p}\triangleq p(x)\triangleright\,\begin{array}{|c||cc|} @@ -1402,8 +1365,8 @@ \subsubsection{Statement \label{subsec:Statement-filter-to-deflate-equivalence}\ }) match { case Some(x) => x } // Compute function composition: - == p(x) match { case true => x } // Rewrite this code equivalently as - == x match { case x if p(x) => x } // $\color{dkgreen}x\triangleright\text{id}_{|p}$ + == p(x) match { case true => x } // Rewrite this code equivalently as + == x match { case x if p(x) => x } // $\color{dkgreen}x\triangleright\text{id}_{|p}$ \end{lstlisting} We can now finish the calculation in Eq.~(\ref{eq:filter-prime-derivation-1}): \begin{align*} @@ -1442,44 +1405,38 @@ \subsubsection{Statement \label{subsec:Statement-deflate-to-filter-equivalence}\ applied. To achieve that, we need a law that switches the order of lifted function compositions around \lstinline!deflate!. The naturality law~(\ref{eq:naturality-law-of-filter}) of \lstinline!filter! has -that form, so we can try - -\begin{wrapfigure}{L}{0.25\columnwidth}% +that form, so we can try deriving a similar naturality law for \lstinline!deflate!. +To switch the order of composition of \lstinline!deflate! with a +lifted function, the law must have the form:\begin{wrapfigure}{L}{0.3\columnwidth}% \vspace{-2\baselineskip} \[ \xymatrix{\xyScaleY{1.0pc}\xyScaleX{5.0pc}F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{deflate}} & F^{A}\ar[d]\sb(0.45){f^{\uparrow F}}\\ \text{???}\ar[r]\sp(0.5){\text{deflate}} & F^{B} } \] -\vspace{-1.5\baselineskip} +\vspace{0.3\baselineskip} \end{wrapfigure}% -\noindent deriving a similar naturality law for \lstinline!deflate!. -To switch the order of composition of \lstinline!deflate! with a -lifted function, the law must have the form: +~\vspace{-0.45\baselineskip} \[ \text{deflate}\bef f^{\uparrow F}=(\text{???})^{\uparrow F}\bef\text{deflate}\quad, \] -as illustrated by the type diagram at left. The types match in the -right-hand side only if the argument of \lstinline!deflate! is of -type $F^{\bbnum 1+B}$. So, the law must have the form $\text{deflate}\bef f^{\uparrow F}=(\text{???}^{:\bbnum 1+A\rightarrow\bbnum 1+B})^{\uparrow F}\bef\text{deflate}$. +as illustrated by the type diagram on the left. The types will match +in the right-hand side only if the argument of \lstinline!deflate! +is of type $F^{\bbnum 1+B}$. So, the law must have the form: +\[ +\text{deflate}\bef f^{\uparrow F}=(\text{???}^{:\bbnum 1+A\rightarrow\bbnum 1+B})^{\uparrow F}\bef\text{deflate}\quad. +\] The typed hole $\text{???}^{:\bbnum 1+A\rightarrow\bbnum 1+B}$ must be filled with a value, say, $g^{:\bbnum 1+A\rightarrow\bbnum 1+B}$, which is somehow related to $f$. The only way to obtain $g$ is to lift the function $f$ to the \lstinline!Option! functor, i.e., to -define $g\triangleq f^{\uparrow\text{Opt}}$. - -\newpage{} - -\begin{comment} -This sentence is always on the previous page unless we force a page -break. -\end{comment} -So, the \textbf{naturality law}\index{naturality law!of deflate@of \texttt{deflate}} -of \lstinline!deflate! is: +define $g\triangleq f^{\uparrow\text{Opt}}$. So, the \textbf{naturality +law}\index{naturality law!of deflate@of \texttt{deflate}} of \lstinline!deflate! +is: -\begin{wrapfigure}{L}{0.25\columnwidth}% -\vspace{-1.65\baselineskip} +\begin{wrapfigure}{L}{0.3\columnwidth}% +\vspace{-2\baselineskip} \[ \xymatrix{\xyScaleY{1.5pc}\xyScaleX{5.0pc}F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{deflate}}\ar[d]\sp(0.45){(f^{\uparrow\text{Opt}})^{\uparrow F}} & F^{A}\ar[d]\sb(0.45){f^{\uparrow F}}\\ F^{\bbnum 1+B}\ar[r]\sp(0.5){\text{deflate}} & F^{B} @@ -1495,7 +1452,7 @@ \subsubsection{Statement \label{subsec:Statement-deflate-to-filter-equivalence}\ where $f^{:A\rightarrow B}$ is an arbitrary function. Assuming that the naturality law~(\ref{eq:naturality-law-of-deflate}) -holds for \lstinline!deflate!, we continue the derivation in Eq.~(\ref{eq:deflate-prime-derivation-2}) +holds, we continue the derivation in Eq.~(\ref{eq:deflate-prime-derivation-2}) towards showing that \lstinline!deflate!$^{\prime}=$ \lstinline!deflate!: \begin{align} {\color{greenunder}\text{expect to equal }\text{deflate}:}\quad & \text{deflate}^{\prime}=\psi_{\text{nonEmpty}}^{\uparrow F}\bef\gunderline{\text{deflate}\bef\text{get}^{\uparrow F}}\nonumber \\ @@ -1546,12 +1503,12 @@ \subsubsection{Statement \label{subsec:Statement-deflate-to-filter-equivalence}\ where we expanded the matrix to accommodate the disjunctive type $\bbnum 1+A$. Then we compute: \begin{align*} - & x^{:\bbnum 1+A}\triangleright\psi_{\text{nonEmpty}}=\text{nonEmpty}\left(x\right)\triangleright\,\begin{array}{|c||cc|} + & x^{:\bbnum 1+A}\triangleright\psi_{\text{nonEmpty}}=\gunderline{\text{nonEmpty}\left(x\right)}\triangleright\,\begin{array}{|c||cc|} & \bbnum 1 & \bbnum 1+A\\ \hline \bbnum 1 & \text{id} & \bbnum 0\\ \bbnum 1 & \bbnum 0 & 1\rightarrow x \end{array}\\ -{\color{greenunder}\text{definition~(\ref{eq:def-of-nonEmpty-and-get}) of }\text{nonEmpty}:}\quad & =x\triangleright\,\begin{array}{|c||cc|} +{\color{greenunder}\text{definition~(\ref{eq:def-of-nonEmpty-and-get})}:}\quad & =x\triangleright\,\begin{array}{|c||cc|} & \bbnum 1 & \bbnum 1\\ \hline \bbnum 1 & \text{id} & \bbnum 0\\ A & \bbnum 0 & \_\rightarrow1 @@ -1568,10 +1525,10 @@ \subsubsection{Statement \label{subsec:Statement-deflate-to-filter-equivalence}\ & \bbnum 1 & \bbnum 1 & A\\ \hline \bbnum 1 & \text{id} & \bbnum 0 & \bbnum 0\\ A & \bbnum 0 & \bbnum 0 & \text{id} -\end{array}\quad, +\end{array}\quad. \end{align*} -where the last matrix uses the fact that $x$ matches the type $\bbnum 0+A$ -in the bottom row. Finally, +The last matrix was derived using the fact that $x$ matches the bottom +row where the type is $\bbnum 0+A$. Finally: \begin{align} & x^{:\bbnum 1+A}\triangleright\psi_{\text{nonEmpty}}\bef\text{get}^{\uparrow\text{Opt}}=x\triangleright\,\begin{array}{|c||ccc|} & \bbnum 1 & \bbnum 1 & A\\ @@ -1609,12 +1566,12 @@ \subsubsection{Statement \label{subsec:Statement-partial-functionlaw-deflate-to- \subparagraph{Proof} -Assume that a \lstinline!deflate! function is given, and define \lstinline!filter! +Given a \lstinline!deflate! function, define \lstinline!filter! through Eq.~(\ref{eq:def-filter-through-deflate}). Then the partial function law~(\ref{eq:partial-function-law-of-filter}) is transformed into an equation illustrated by the following diagram: \[ -\xymatrix{\xyScaleY{0.1pc}\xyScaleX{3pc} & F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{deflate}} & F^{A}\ar[rd]\sp(0.5){~(f^{:A\rightarrow B})^{\uparrow F}}\\ +\xymatrix{\xyScaleY{0.1pc}\xyScaleX{2.8pc} & F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{deflate}} & F^{A}\ar[rd]\sp(0.5){~(f^{:A\rightarrow B})^{\uparrow F}}\\ F^{A}\ar[ru]\sp(0.5){\psi_{p}^{\uparrow F}}\ar[rd]\sb(0.5){\psi_{p}^{\uparrow F}} & & & F^{B} & \psi_{p}^{\uparrow F}\bef\text{deflate}\bef f^{\uparrow F}=\psi_{p}^{\uparrow F}\bef\text{deflate}\bef f_{|p}^{\uparrow F}\quad.\\ & F^{\bbnum 1+A}\ar[r]\sp(0.5){\text{deflate}} & F^{A}\ar[ru]\sb(0.5){(f_{|p}^{:A\rightarrow B})^{\uparrow F}} } @@ -1643,7 +1600,7 @@ \subsubsection{Statement \label{subsec:Statement-partial-functionlaw-deflate-to- \end{lstlisting} The same calculation in the code notation looks like this: \begin{align*} - & x^{:A}\triangleright\psi_{p}\bef f^{\uparrow\text{Opt}}=p(x)\triangleright\,\begin{array}{|c||cc|} +x^{:A}\triangleright\psi_{p}\bef f^{\uparrow\text{Opt}} & =p(x)\triangleright\,\begin{array}{|c||cc|} & \bbnum 1 & A\\ \hline \bbnum 1\,(\text{false}) & \text{id} & \bbnum 0\\ \bbnum 1\,(\text{true}) & \bbnum 0 & 1\rightarrow x @@ -1651,7 +1608,8 @@ \subsubsection{Statement \label{subsec:Statement-partial-functionlaw-deflate-to- & \bbnum 1 & A\\ \hline \bbnum 1 & \text{id} & \bbnum 0\\ A & \bbnum 0 & f -\end{array}\,=p(x)\triangleright\,\begin{array}{|c||cc|} +\end{array}\\ + & =p(x)\triangleright\,\,\begin{array}{|c||cc|} & \bbnum 1 & A\\ \hline \bbnum 1\,(\text{false}) & \text{id} & \bbnum 0\\ \bbnum 1\,(\text{true}) & \bbnum 0 & 1\rightarrow f(x) @@ -1791,8 +1749,7 @@ \subsection{Motivation and laws for \texttt{liftOpt}\label{subsec:Motivation-and a more general type signature, $f^{:A\rightarrow\bbnum 1+B}$, and composing $f^{\uparrow F}$ with \lstinline!deflate! into a function that maps $F^{A}\rightarrow F^{B}$. It turns out that the resulting -function, which we will call \lstinline!liftOpt! and denote by $\text{liftOpt}_{F}$ -or simply by $\text{liftOpt}$: +function, which we will call \lstinline!liftOpt! and denote by $\text{liftOpt}_{F}$: \begin{lstlisting} def liftOpt_F[A, B](f: A => Option[B]): F[A] => F[B] = _.map(f).deflate \end{lstlisting} @@ -1819,24 +1776,15 @@ \subsection{Motivation and laws for \texttt{liftOpt}\label{subsec:Motivation-and We will now derive some properties of \lstinline!liftOpt!. The definition of \lstinline!liftOpt! through \lstinline!map! and \lstinline!deflate! -is - -\begin{wrapfigure}{L}{0.28\columnwidth}% -\vspace{-1.85\baselineskip} -\begin{comment} --200baselineskip\% if inside the page, -85 if at start of page -\end{comment} +is illustrated by the diagram: \[ \xymatrix{\xyScaleY{1.4pc}\xyScaleX{4.4pc}F^{A}\ar[r]\sp(0.5){(f^{:A\rightarrow\bbnum 1+B})^{\uparrow F}}\ar[rd]\sb(0.4){\text{liftOpt}\,(f)\triangleq~~} & F^{\bbnum 1+B}\ar[d]\sp(0.4){\text{deflate}}\\ & F^{B} } \] -\vspace{-2\baselineskip} -\end{wrapfigure}% - -\noindent illustrated in the diagram at left. Since $f$ is arbitrary -in Eq.~(\ref{eq:def-liftOpt-via-deflate}), we may set $f=\text{id}^{:A\rightarrow A}$ -and the type parameter $A=\bbnum 1+B$ to find +Since $f$ is arbitrary in Eq.~(\ref{eq:def-liftOpt-via-deflate}), +we may set $f=\text{id}^{:A\rightarrow A}$ and the type parameter +$A=\bbnum 1+B$ to find: \begin{equation} \text{liftOpt}^{\bbnum 1+B,B}(\text{id}^{:\bbnum 1+B\rightarrow\bbnum 1+B})=\gunderline{\text{id}^{\uparrow F}}\bef\text{deflate}=\text{deflate}\quad.\label{eq:def-deflate-via-liftOpt} \end{equation} @@ -1890,26 +1838,20 @@ \subsubsection{Statement \label{subsec:Statement-liftOpt-equivalent-to-deflate}\ \[ (h^{:A\rightarrow B})^{\uparrow F}\bef\text{liftOpt}^{B,C}(f^{:B\rightarrow\bbnum 1+C})=h^{\uparrow F}\bef f^{\uparrow F}\bef\text{deflate}=(h\bef f)^{\uparrow F}\bef\text{deflate}=\text{liftOpt}^{A,C}(h\bef f)\quad. \] - -\begin{wrapfigure}{L}{0.24\columnwidth}% -\vspace{-2.5\baselineskip} \[ \xymatrix{\xyScaleY{1.8pc}\xyScaleX{4.5pc}F^{A}\ar[d]\sb(0.5){h^{\uparrow F}}\ar[rd]\sp(0.5){~~\text{ liftOpt}\,(h\bef f)}\\ F^{B}\ar[r]\sp(0.42){\text{liftOpt}\,(f)} & F^{C} } \] -\vspace{-2\baselineskip} -\end{wrapfigure}% - -\noindent It follows that \emph{if} \lstinline!liftOpt! were defined -via \lstinline!deflate! then \lstinline!liftOpt! would automatically -satisfy the naturality law\index{naturality law!of liftOpt@of \texttt{liftOpt}} -(see diagram at left) +It follows that \emph{if} \lstinline!liftOpt! were defined via \lstinline!deflate! +then \lstinline!liftOpt! would automatically satisfy the naturality +law\index{naturality law!of liftOpt@of \texttt{liftOpt}} (see diagram +at left): \begin{equation} (h^{:A\rightarrow B})^{\uparrow F}\bef\text{liftOpt}_{F}^{B,C}(f^{:B\rightarrow\bbnum 1+C})=\text{liftOpt}_{F}^{A,C}(h^{:A\rightarrow B}\bef f^{:B\rightarrow\bbnum 1+C})\quad.\label{eq:left-naturality-law-of-liftOpt} \end{equation} This motivates \emph{imposing} that law on \lstinline!liftOpt!. We -can then finish the proof, resuming from Eq.~(\ref{eq:liftOpt-equivalent-deflate-derivation-1}), +can then finish the proof, resuming from Eq.~(\ref{eq:liftOpt-equivalent-deflate-derivation-1}): \begin{align*} {\color{greenunder}\text{expect to equal }\text{liftOpt}\,(f):}\quad & \text{liftOpt}^{\prime}(f)=\gunderline{f^{\uparrow F}}\bef\text{liftOpt}\,(\gunderline{\text{id}})=\text{liftOpt}\,(\gunderline{f\bef\text{id}})=\text{liftOpt}\,(f)\quad. \end{align*} @@ -1933,7 +1875,7 @@ \subsubsection{Statement \label{subsec:Statement-liftOpt-equivalent-to-deflate}\ \text{filt}\,(p^{:A\rightarrow\bbnum 2})=\psi_{p}^{\uparrow F}\bef\text{deflate}=\text{liftOpt}\,(\psi_{p})\quad.\label{eq:filter-via-liftOpt} \end{equation} Conversely, \lstinline!liftOpt! is expressed via \lstinline!filter! -like this, +like this: \begin{equation} \text{liftOpt}^{A,B}(f^{:A\rightarrow\bbnum 1+B})\triangleq f^{\uparrow F}\bef\text{deflate}=f^{\uparrow F}\bef\text{filt}\,(\psi_{\text{nonEmpty}})\quad,\label{eq:def-liftOpt-via-filter} \end{equation} @@ -1950,7 +1892,7 @@ \subsubsection{Statement \label{subsec:Statement-liftOpt-equivalent-to-deflate}\ \] The function $\psi_{(\_\rightarrow\text{true})}$ is equivalent to a simpler function $x^{:A}\rightarrow\bbnum 0+x$ (i.e., \lstinline!x => Some(x)! -in Scala), +in Scala): \begin{align} {\color{greenunder}\text{use Eq.~(\ref{eq:def-of-psi})}:}\quad & x^{:A}\triangleright\psi_{(\_\rightarrow\text{true})}=x^{:A}\triangleright(\_^{:A}\rightarrow\gunderline{\text{true}^{:\text{Opt}^{\bbnum 1}}})\bef(1\rightarrow x)^{\uparrow\text{Opt}}\nonumber \\ {\color{greenunder}\text{use equivalence }\bbnum 0+1\cong\text{true}^{:\text{Opt}^{\bbnum 1}}:}\quad & =\gunderline{x^{:A}\triangleright(\_^{:A}\rightarrow\bbnum 0+1)}\bef(1\rightarrow x)^{\uparrow\text{Opt}}\nonumber \\ @@ -1964,7 +1906,7 @@ \subsubsection{Statement \label{subsec:Statement-liftOpt-equivalent-to-deflate}\ \bbnum 0 & x\end{array}\,=\bbnum 0+x^{:A}\quad.\label{eq:psi-of-true-equals-Some-derivation} \end{align} So, we expect the \textbf{identity law} of\index{identity laws!of liftOpt@of \texttt{liftOpt}} -\lstinline!liftOpt! to be +\lstinline!liftOpt! to be: \begin{equation} \text{liftOpt}^{A,A}(x^{:A}\rightarrow\bbnum 0+x)=\text{id}^{:F^{A}\rightarrow F^{A}}\quad.\label{eq:identity-law-of-liftOpt} \end{equation} @@ -2010,7 +1952,7 @@ \subsubsection{Statement \label{subsec:Statement-identity-law-of-liftOpt}\ref{su \] Then \lstinline!liftOpt!\textsf{'}s \index{identity laws!of liftOpt@of \texttt{liftOpt}}identity law~(\ref{eq:identity-law-of-liftOpt}) is written more concisely -as +as: \begin{equation} \text{liftOpt}_{F}(\text{pu}_{\text{Opt}})=\text{id}\quad.\label{eq:identity-law-liftOpt-via-pure-puOpt} \end{equation} @@ -2022,7 +1964,7 @@ \subsubsection{Statement \label{subsec:Statement-identity-law-of-liftOpt}\ref{su f^{\uparrow F}\bef\text{liftOpt}_{F}(\text{pu}_{\text{Opt}})=\text{liftOpt}_{F}(f\bef\text{pu}_{\text{Opt}})\quad. \] So the \textsf{``}combined\textsf{''} \textbf{naturality-identity law} of\index{naturality law!combined with identity law} -\lstinline!liftOpt! is +\lstinline!liftOpt! is: \begin{equation} \text{liftOpt}_{F}(f^{:A\rightarrow B}\bef\text{pu}_{\text{Opt}}^{B})=f^{\uparrow F}\quad.\label{eq:combined-naturality-identity-law-of-liftOpt} \end{equation} @@ -2037,7 +1979,7 @@ \subsubsection{Statement \label{subsec:Statement-identity-law-of-liftOpt}\ref{su uses a predicate $p$ only through the function $\psi_{p}$. So, we will derive the composition law of \lstinline!liftOpt! if we somehow express $\psi_{p}$ as a combination of $\psi_{p_{1}}$ and $\psi_{p_{2}}$. -Begin by writing +Begin by writing: \begin{lstlisting} psi(p) == { x => Some(x).filter(p) } == { x => Some(x).filter(p1).filter(p2) } \end{lstlisting} @@ -2056,7 +1998,7 @@ \subsubsection{Statement \label{subsec:Statement-identity-law-of-liftOpt}\ref{su x => Some(x).filter(p1).map(y => Some(y).filter(p2)) // Type is Option[Option[A]]. \end{lstlisting} Except for the type \lstinline!Option[Option[A]]!, the result is -correct: a value \lstinline!x:A! will be present within the \lstinline!Option[Option[A]]! +correct: a value \lstinline!x: A! will be present within the \lstinline!Option[Option[A]]! wrapper only if \emph{both} \lstinline!p1(x)! and \lstinline!p2(x)! return \lstinline!true!. To convert the result to the required type \lstinline!Option[A]!, we apply \lstinline!Option!\textsf{'}s method \lstinline!flatten!: @@ -2066,14 +2008,14 @@ \subsubsection{Statement \label{subsec:Statement-identity-law-of-liftOpt}\ref{su == psi(p1) andThen (_.flatMap(psi(p2))) // Using standard flatten and flatMap for Option. \end{lstlisting} Denote this combination of the functions $\psi_{p_{1}}$ and $\psi_{p_{2}}$ -by the symbol $\diamond_{_{\text{Opt}}}$, so that we may write +by the symbol $\diamond_{_{\text{Opt}}}$, so that we may write: \[ \psi_{p}=\psi_{p_{1}}\diamond_{_{\text{Opt}}}\psi_{p_{2}}\triangleq x^{:A}\rightarrow x\triangleright\psi_{p_{1}}\triangleright\text{flm}_{\text{Opt}}(\psi_{p_{2}})=\psi_{p_{1}}\bef(y\rightarrow y\triangleright\text{flm}_{\text{Opt}}(\psi_{p_{2}}))\quad. \] We use the symbol $\text{flm}_{\text{Opt}}$ for \lstinline!Option!\textsf{'}s \lstinline!flatMap!; the Scala code \lstinline!x.flatMap(f)! is denoted by $x\triangleright\text{flm}_{\text{Opt}}(f)$ if we view -$\text{flm}_{\text{Opt}}$ as a curried function with the type signature +$\text{flm}_{\text{Opt}}$ as a curried function with the type signature: \[ \text{flm}_{\text{Opt}}:(A\rightarrow\text{Opt}^{B})\rightarrow\text{Opt}^{A}\rightarrow\text{Opt}^{B}\quad. \] @@ -2106,7 +2048,7 @@ \subsubsection{Statement \label{subsec:Statement-identity-law-of-liftOpt}\ref{su {\color{greenunder}\text{right-hand side}:}\quad & \text{filt}\,(p)=\text{liftOpt}\,(\psi_{p})=\text{liftOpt}\,(\psi_{p_{1}}\diamond_{_{\text{Opt}}}\psi_{p_{2}})\quad. \end{align*} If \lstinline!filter!\textsf{'}s composition law holds, we obtain (without -any additional assumptions) the equation +any additional assumptions) the equation: \begin{equation} \text{liftOpt}\,(\psi_{p_{1}})\bef\text{liftOpt}\,(\psi_{p_{2}})=\text{liftOpt}\,(\psi_{p_{1}}\diamond_{_{\text{Opt}}}\psi_{p_{2}})\quad.\label{eq:restricted-composition-law-for-liftOpt} \end{equation} @@ -2116,22 +2058,12 @@ \subsubsection{Statement \label{subsec:Statement-identity-law-of-liftOpt}\ref{su in a more general way by allowing arbitrary $f^{:A\rightarrow\bbnum 1+B}$ and $g^{:B\rightarrow\bbnum 1+C}$ instead of specific functions $\psi_{p_{1}}$ and $\psi_{p_{2}}$. - -\begin{wrapfigure}{L}{0.28\columnwidth}% -\vspace{-1.85\baselineskip} -\begin{comment} --200 if inside page, -120 if at start of page -\end{comment} \[ \xymatrix{\xyScaleY{1.2pc}\xyScaleX{1.8pc} & F^{B}\ar[rd]\sp(0.55){\ \text{liftOpt}\,(g)}\\ F^{A}\ar[ru]\sp(0.45){\text{liftOpt}\,(f)\ }\ar[rr]\sb(0.5){\text{liftOpt}\,(f\diamond_{_{\text{Opt}}}g)} & & F^{C} } \] -\vspace{-2.15\baselineskip} -\end{wrapfigure}% - -\noindent The composition law of \lstinline!liftOpt! is then written -as\index{composition law!of liftOpt@of \texttt{liftOpt}} +The composition law of \lstinline!liftOpt! is then written as:\index{composition law!of liftOpt@of \texttt{liftOpt}} \begin{equation} \text{liftOpt}_{F}(f^{:A\rightarrow\bbnum 1+B})\bef\text{liftOpt}_{F}(g^{:B\rightarrow\bbnum 1+C})=\text{liftOpt}_{F}(f\diamond_{_{\text{Opt}}}g)\quad.\label{eq:composition-law-of-liftOpt} \end{equation} @@ -2155,7 +2087,7 @@ \subsubsection{Statement \label{subsec:Statement-2-laws-of-liftOpt-entail-other- \subparagraph{Proof} \textbf{(a)} Choose functions $f^{:A\rightarrow\bbnum 1+B}$ to be -of the form +of the form: \begin{align*} & f^{:A\rightarrow\bbnum 1+B}\triangleq h^{:A\rightarrow B}\bef\text{pu}_{\text{Opt}}^{:B\rightarrow\bbnum 1+B}\\ {\color{greenunder}\text{or, written out in more detail}:}\quad & =h^{:A\rightarrow B}\bef(x^{:B}\rightarrow\bbnum 0+x)=x^{:A}\rightarrow\bbnum 0+h(x)\quad, @@ -2171,12 +2103,12 @@ \subsubsection{Statement \label{subsec:Statement-2-laws-of-liftOpt-entail-other- the standard \lstinline!flatMap! method of the \lstinline!Option! type. With the notation $\text{flm}_{\text{Opt}}$ for the curried \lstinline!flatMap! function, the definition of $\diamond_{_{\text{Opt}}}$ -is simplified to +is simplified to: \begin{equation} f^{:A\rightarrow\bbnum 1+B}\diamond_{_{\text{Opt}}}g^{:B\rightarrow\bbnum 1+C}\triangleq f\bef\text{flm}_{\text{Opt}}(g)\quad.\label{eq:def-of-Kleisli-product} \end{equation} Then we compute $f\diamond_{_{\text{Opt}}}g$ using this definition -of $\diamond_{_{\text{Opt}}}$ as +of $\diamond_{_{\text{Opt}}}$ as: \begin{align*} {\color{greenunder}\text{definition of }f:}\quad & \gunderline f\diamond_{_{\text{Opt}}}g=(h\bef\text{pu}_{\text{Opt}})\,\gunderline{\diamond_{_{\text{Opt}}}}\,g=h\bef\gunderline{\text{pu}_{\text{Opt}}\bef\text{flm}_{\text{Opt}}}(g)\\ {\color{greenunder}\text{compute composition (see below)}:}\quad & =h\bef g\quad. @@ -2230,7 +2162,7 @@ \subsubsection{Statement \label{subsec:Statement-2-laws-of-liftOpt-entail-other- by eliminating one of the \lstinline!liftOpt! functions. \textbf{(b)} We keep $f^{:A\rightarrow\bbnum 1+B}$ arbitrary but -choose $g^{:B\rightarrow\bbnum 1+C}$ to be of the form +choose $g^{:B\rightarrow\bbnum 1+C}$ to be of the form: \[ g^{:B\rightarrow\bbnum 1+C}\triangleq h^{:B\rightarrow C}\bef\text{pu}_{\text{Opt}}^{C}\quad, \] @@ -2241,7 +2173,7 @@ \subsubsection{Statement \label{subsec:Statement-2-laws-of-liftOpt-entail-other- {\color{greenunder}\text{use Eq.~(\ref{eq:combined-naturality-identity-law-of-liftOpt})}:}\quad & \quad=\text{liftOpt}\,(f)\bef h^{\uparrow F}\quad. \end{align*} To proceed, we need to compute the Kleisli composition $f\diamond_{_{\text{Opt}}}g$ -for the chosen form of $g$, +for the chosen form of $g$: \begin{align*} & f\diamond_{_{\text{Opt}}}\gunderline g=f\diamond_{_{\text{Opt}}}(h\bef\text{pu}_{\text{Opt}})\\ {\color{greenunder}\text{definition~(\ref{eq:def-of-Kleisli-product}) of }\diamond_{_{\text{Opt}}}:}\quad & =f\bef\text{flm}_{\text{Opt}}(h\bef\gunderline{\text{pu}_{\text{Opt}}})=f\bef\text{flm}_{\text{Opt}}(y\rightarrow\bbnum 0+h(y))\\ @@ -2258,20 +2190,12 @@ \subsubsection{Statement \label{subsec:Statement-2-laws-of-liftOpt-entail-other- {\color{greenunder}\text{definition of lifting, }^{\uparrow\text{Opt}}:}\quad & =f\bef h^{\uparrow\text{Opt}}\quad. \end{align*} This yields the \textbf{right naturality} law\index{naturality law!of liftOpt@of \texttt{liftOpt}} -of \lstinline!liftOpt!, - -\begin{wrapfigure}{L}{0.3\columnwidth}% -\vspace{-1.4\baselineskip} +of \lstinline!liftOpt!: \[ \xymatrix{\xyScaleY{1.4pc}\xyScaleX{4.5pc}F^{A}\ar[d]\sb(0.5){\text{liftOpt}\,(f)}\ar[rd]\sp(0.5){~~~\text{ liftOpt}\,(f\bef h^{\uparrow\text{Opt}})}\\ F^{B}\ar[r]\sp(0.42){h^{\uparrow F}} & F^{C} } \] -\vspace{-0.5\baselineskip} -\end{wrapfigure}% - -\vspace{-0.6\baselineskip} -~ \begin{equation} \text{liftOpt}_{F}(f^{:A\rightarrow\bbnum 1+B})\bef h^{\uparrow F}=\text{liftOpt}_{F}(f\bef h^{\uparrow\text{Opt}})\quad.\label{eq:right-naturality-law-of-liftOpt} \end{equation} @@ -2367,23 +2291,16 @@ \subsection{Constructions of filterable functors\label{subsec:Constructions-of-f \subsubsection{Statement \label{subsec:Statement-filterable-composition-functors}\ref{subsec:Statement-filterable-composition-functors}} The functor $F^{A}\triangleq G^{H^{A}}$ is filterable when $H^{A}$ -is filterable and $G^{A}$ is any functor. +is filterable and $G^{A}$ is \emph{any} functor. \subparagraph{Proof} Assuming that $\text{liftOpt}_{H}$ is available and lawful, we define -$\text{liftOpt}_{F}$ as - -\begin{wrapfigure}{L}{0.61\columnwidth}% -\vspace{-0.9\baselineskip} +$\text{liftOpt}_{F}$ as: \begin{lstlisting} def liftOpt_F[A, B](f: A => Option[B]): G[H[A]] => G[H[B]] = { g: G[H[A]] => g.map(liftOpt_H(f)) } \end{lstlisting} -\vspace{-0.3\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.9\baselineskip} \[ \text{liftOpt}_{F}(f)\triangleq\big(\text{liftOpt}_{H}(f)\big)^{\uparrow G}\quad. \] @@ -2416,7 +2333,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-functor-product}\ref \subparagraph{Proof} To define $\text{liftOpt}_{F}$, we use the pair product operation -$\boxtimes$ similarly to Eq.~(\ref{eq:def-of-functor-product-fmap}), +$\boxtimes$ similarly to Eq.~(\ref{eq:def-of-functor-product-fmap}): \[ \text{liftOpt}_{F}(p)\triangleq\text{liftOpt}_{G}(p)\boxtimes\text{liftOpt}_{H}(p)\quad. \] @@ -2467,7 +2384,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct}\ref{subse \subparagraph{Proof} Assuming that $\text{liftOpt}_{G}$ and $\text{liftOpt}_{H}$ are -available, we define $\text{liftOpt}_{F}$ as +available, we define $\text{liftOpt}_{F}$ as: \begin{lstlisting} def liftOpt_F[A, B](f: A => Option[B]): Either[G[A], H[A]] => Either[G[B], H[B]] = { case Left(ga) => liftOpt_G(f)(ga) @@ -2481,7 +2398,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct}\ref{subse H^{A} & \bbnum 0 & \text{liftOpt}_{H}(f) \end{array}\quad. \] -Lifting to the functor $F^{A}$ is defined as in Statement~\ref{subsec:functor-Statement-functor-coproduct}, +Lifting to the functor $F^{A}$ is defined as in Statement~\ref{subsec:functor-Statement-functor-coproduct}: \[ f^{\uparrow F}\triangleq\begin{array}{|c||cc|} & G^{A} & H^{A}\\ @@ -2532,11 +2449,11 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub \subparagraph{Proof} Assuming that $\text{liftOpt}_{G}$ is available, we define $\text{liftOpt}_{F}$ -as +as: \begin{lstlisting} def liftOpt_F[A, B](f: A => Option[B]): Option[(A, G[A])] => Option[(B, G[B])] = { case None => None // An empty wrapper remains empty. - case Some((a, ga)) => f(a) match { // Does `a` pass the filtering predicate? + case Some((a, ga)) => f(a) match { // Does `a` satisfy the predicate? case None => None // No. Drop all data, return an empty wrapper. case Some(b) => Some((b, liftOpt_G(f)(ga))) // Yes. Keep `b` and filter `ga` using `liftOpt_G`. } @@ -2564,7 +2481,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub way. Note that the code pattern when destructuring an \lstinline!Option! -value looks like this function, +value looks like this function: \begin{lstlisting} def p[A, B, C](q: A => Option[B], r: B => C): Option[A] => Option[C] = { case None => None @@ -2579,7 +2496,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub \begin{lstlisting} def p[A, B, C](q: A => Option[B], r: B => C): Option[A] => Option[C] = _.flatMap { a => q(a).map(r) } \end{lstlisting} -So we may write the code of \lstinline!liftOpt_F! as +So we may write the code of \lstinline!liftOpt_F! as: \begin{lstlisting} def liftOpt_F[A, B](f: A => Option[B]): Option[(A, G[A])] => Option[(B, G[B])] = _.flatMap { case (a, ga) => f(a).map { b => (b, liftOpt_G(f)(ga)) } @@ -2630,7 +2547,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub \end{lstlisting} Additional work is necessary to check \lstinline!liftOpt!\textsf{'}s composition -law, +law: \begin{equation} \text{liftOpt}_{F}(f)\bef\text{liftOpt}_{F}(f^{\prime})=\text{liftOpt}_{F}(f\diamond_{_{\text{Opt}}}f^{\prime})\quad.\label{eq:liftOpt-composition-law-derivation1} \end{equation} @@ -2654,7 +2571,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub \text{liftOpt}_{\text{Opt}}(f)=f^{\uparrow\text{Opt}}\bef\text{deflate}_{\text{Opt}}=f^{\uparrow\text{Opt}}\bef\text{ftn}_{\text{Opt}}=\text{flm}_{\text{Opt}}(f)\quad. \] So, \lstinline!Option!\textsf{'}s \lstinline!flatMap! method equals \lstinline!liftOpt! -and obeys a law similar to \lstinline!liftOpt!\textsf{'}s composition law, +and obeys a law similar to \lstinline!liftOpt!\textsf{'}s composition law: \begin{equation} \text{flm}_{\text{Opt}}(f)\bef\text{flm}_{\text{Opt}}(f^{\prime})=\text{flm}_{\text{Opt}}(f\diamond_{_{\text{Opt}}}f^{\prime})=\text{flm}_{\text{Opt}}\big(f\bef\text{flm}_{\text{Opt}}(f^{\prime})\big)\quad.\label{eq:associativity-law-of-flatMap-for-Option} \end{equation} @@ -2664,7 +2581,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub below. To make the calculation quicker, denote by $r_{f,g}$ a sub-expression -in Eq.~(\ref{eq:liftOpt-flm-opt-derivation1}), so that we can write +in Eq.~(\ref{eq:liftOpt-flm-opt-derivation1}), so that we can write: \begin{equation} r_{f,g}\triangleq b\rightarrow b\times\text{liftOpt}_{G}(f)(g)\quad,\quad\quad\text{liftOpt}_{F}(f)=\text{flm}_{\text{Opt}}(a\times g\rightarrow a\triangleright f\bef r_{f,g}^{\uparrow\text{Opt}})\quad.\label{eq:liftOpt-short-flatmap-opt-derivation1} \end{equation} @@ -2698,7 +2615,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub of $\text{flm}_{\text{Opt}}$. If we somehow bring those functions inside $\text{flm}_{\text{Opt}}(...)$, we may be able to simplify them further. So, we look for properties of \lstinline!Option!\textsf{'}s -\lstinline!flatMap! that should have the form +\lstinline!flatMap! that should have the form: \begin{align*} & (p^{:A\rightarrow B})^{\uparrow\text{Opt}}\bef\text{flm}_{\text{Opt}}(q^{:B\rightarrow\text{Opt}^{C}})=\text{flm}_{\text{Opt}}(\text{???}^{:A\rightarrow\text{Opt}^{C}})\quad,\\ & \text{flm}_{\text{Opt}}(p^{:A\rightarrow\text{Opt}^{B}})\bef(q^{:B\rightarrow C})^{\uparrow\text{Opt}}=\text{flm}_{\text{Opt}}(\text{???}^{:A\rightarrow\text{Opt}^{C}})\quad. @@ -2717,13 +2634,13 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub {\color{greenunder}\text{expand }r_{f,g}:}\quad & =\text{flm}_{\text{Opt}}\big((a\rightarrow a\times\text{liftOpt}_{G}(f)(g))\bef\big(a^{\prime}\times g^{\prime}\rightarrow a^{\prime}\triangleright f^{\prime}\bef r_{f^{\prime},g^{\prime}}^{\uparrow\text{Opt}}\big)\big)\\ {\color{greenunder}\text{compute composition}:}\quad & =\text{flm}_{\text{Opt}}\big(a\rightarrow a\triangleright f^{\prime}\bef r_{f^{\prime},\text{liftOpt}_{G}(f)(g)}^{\uparrow\text{Opt}}\big)\quad, \end{align*} -and +and: \begin{align*} & \text{flm}_{\text{Opt}}(f^{\prime})\,\gunderline{\bef r_{f\bef\text{flm}_{\text{Opt}}(f^{\prime}),g}^{\uparrow\text{Opt}}}=\text{flm}_{\text{Opt}}\big(\gunderline{f^{\prime}}\bef r_{f\bef\text{flm}_{\text{Opt}}(f^{\prime}),g}^{\uparrow\text{Opt}}\big)\\ {\color{greenunder}\text{expand function }f^{\prime}:}\quad & =\text{flm}_{\text{Opt}}\big(a\rightarrow a\triangleright f^{\prime}\bef r_{f\bef\text{flm}_{\text{Opt}}(f^{\prime}),g}^{\uparrow\text{Opt}}\big)\quad. \end{align*} -The difference between sub-expressions has become smaller: it just -remains to show that +The difference between sub-expressions has become smaller; it just +remains to show the following: \[ r_{f^{\prime},\text{liftOpt}_{G}(f)(g)}\overset{?}{=}r_{f\bef\text{flm}_{\text{Opt}}(f^{\prime}),g}\quad. \] @@ -2736,7 +2653,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub \text{liftOpt}_{G}(f^{\prime})\big(\text{liftOpt}_{G}(f)(g)\big)\overset{?}{=}\text{liftOpt}_{G}\big(f\bef\text{flm}_{\text{Opt}}(f^{\prime})\big)(g)\quad. \] This is equivalent to $\text{liftOpt}_{G}$\textsf{'}s composition law applied -to the function $g$, +to the function $g$: \[ g\triangleright\text{liftOpt}_{G}(f)\bef\text{liftOpt}_{G}(f^{\prime})=g\triangleright\text{liftOpt}_{G}(\gunderline{f\diamond_{_{\text{Opt}}}f^{\prime}})=g\triangleright\text{liftOpt}_{G}\big(f\bef\text{flm}_{\text{Opt}}(f^{\prime})\big)\quad. \] @@ -2748,7 +2665,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub the pair of type $A\times G^{A}$ needs to pass the filter for any data to remain in the functor after filtering. We can use the same construction repeatedly with $G^{\bullet}\triangleq\bbnum 1$ and -obtain the type +obtain the type: \[ L_{n}^{A}\triangleq\underbrace{\bbnum 1+A\times\left(\bbnum 1+A\times\left(\bbnum 1+...\times(\bbnum 1+A\times\bbnum 1)\right)\right)}_{\text{parameter }A\text{ is used }n\text{ times}}\quad, \] @@ -2762,7 +2679,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub where it was used). We can also generalize the construction of Statement~\ref{subsec:Statement-filterable-coproduct-1} -to the functor +to the functor: \[ F^{A}\triangleq\bbnum 1+\underbrace{A\times A\times...\times A}_{n\text{ times}}\times\,G^{A}\quad. \] @@ -2798,7 +2715,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-coproduct-1}\ref{sub The only way to proceed is to have a function $G^{B}\rightarrow G^{A}$. We cannot obtain such a function by lifting $f$ to the contrafunctor $G$: that gives $f^{\downarrow G}:G^{\bbnum 1+B}\rightarrow G^{A}$. -So, we need to require having a function +So, we need to require having a function: \begin{equation} \text{liftOpt}_{G}(f^{:A\rightarrow\bbnum 1+B}):G^{B}\rightarrow G^{A}\quad.\label{eq:type-signature-liftOpt-contrafunctors} \end{equation} @@ -2847,13 +2764,13 @@ \subsubsection{Statement \label{subsec:Statement-filterable-function-type}\ref{s The only sub-expression that remains different is $\text{\text{liftOpt}}_{G}(f\bef\text{pu}_{\text{Opt}})$, and the derivation will be finished if we assume the \textbf{naturality-identity law} of filterable contrafunctors\index{identity laws!of filterable contrafunctors} -to be +to be: \begin{equation} \text{\text{liftOpt}}_{G}(f\bef\text{pu}_{\text{Opt}})=f^{\downarrow G}\quad.\label{eq:naturality-identity-law-filterable-contrafunctor} \end{equation} The composition law of $F$ applied to a value to a value $p^{:G^{A}\rightarrow H^{A}}$ -is +is: \begin{align*} {\color{greenunder}\text{left-hand side of Eq.~(\ref{eq:composition-law-of-liftOpt}) for }F:}\quad & p\triangleright\text{liftOpt}_{F}(f)\bef\text{liftOpt}_{F}(g)=\gunderline{p\triangleright\text{liftOpt}_{F}(f)}\triangleright\text{liftOpt}_{F}(g)\\ {\color{greenunder}\text{definition~(\ref{eq:def-of-liftopt-function-type}) of }\text{liftOpt}_{F}:}\quad & =\big(\text{\text{liftOpt}}_{G}(f)\bef p\bef\text{liftOpt}_{H}(f)\big)\,\gunderline{\triangleright\text{liftOpt}_{F}(g)}\\ @@ -2861,12 +2778,12 @@ \subsubsection{Statement \label{subsec:Statement-filterable-function-type}\ref{s {\color{greenunder}\text{composition law~(\ref{eq:composition-law-of-liftOpt}) of }\text{liftOpt}_{H}:}\quad & =\text{\text{liftOpt}}_{G}(g)\bef\text{\text{liftOpt}}_{G}(f)\bef p\bef\text{liftOpt}_{H}(f\diamond_{_{\text{Opt}}}g)\quad. \end{align*} The right-hand side of Eq.~(\ref{eq:composition-law-of-liftOpt}) -for $F$ is +for $F$ is: \[ p\triangleright\text{liftOpt}_{F}(f\diamond_{_{\text{Opt}}}g)=\text{liftOpt}_{G}(f\diamond_{_{\text{Opt}}}g)\bef p\bef\text{liftOpt}_{H}(f\diamond_{_{\text{Opt}}}g)\quad. \] Clearly, we need to require the \textbf{composition law} of filterable\index{composition law!of filterable contrafunctor} -contrafunctor $G$ to be +contrafunctor $G$ to be: \begin{equation} \text{\text{liftOpt}}_{G}(g)\bef\text{\text{liftOpt}}_{G}(f)=\text{liftOpt}_{G}(f\diamond_{_{\text{Opt}}}g)\quad.\label{eq:composition-law-filterable-contrafunctor} \end{equation} @@ -2878,7 +2795,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-function-type}\ref{s How to generalize the filtering operation from sequences to other recursive types? For motivation, we look at two examples: the filterable -\lstinline!List! functor defined by +\lstinline!List! functor defined by: \[ \text{List}^{A}\triangleq\bbnum 1+A\times\text{List}^{A}\quad, \] @@ -2888,7 +2805,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-function-type}\ref{s \subsubsection{Statement \label{subsec:Statement-filterable-recursive-type}\ref{subsec:Statement-filterable-recursive-type}} If $G^{\bullet}$ is a filterable functor, the recursive functor $F^{\bullet}$ -defined by +defined by: \[ F^{A}\triangleq G^{A}+A\times F^{A} \] @@ -3008,7 +2925,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-recursive-type}\ref{ call to $\overline{\text{liftOpt}_{F}}$. The right-hand side of Eq.~(\ref{eq:composition-law-of-liftOpt}) -applied to $\bbnum 0+a\times r$ gives +applied to $\bbnum 0+a\times r$ gives: \begin{align*} & (\bbnum 0+a\times r)\triangleright\text{liftOpt}_{F}(f\diamond_{_{\text{Opt}}}g)\\ {\color{greenunder}\text{use Eq.~(\ref{eq:expression-liftOpt-derivation2})}:}\quad & =a\triangleright(f\diamond_{_{\text{Opt}}}g)\triangleright\,\begin{array}{||c|} @@ -3017,7 +2934,7 @@ \subsubsection{Statement \label{subsec:Statement-filterable-recursive-type}\ref{ \end{array}\quad. \end{align*} The only remaining difference compared with the last expression in -Eq.~(\ref{eq:lhs-comp-law-liftOpt-derivation2}) is +Eq.~(\ref{eq:lhs-comp-law-liftOpt-derivation2}) is: \begin{equation} f\bef\,\begin{array}{||c|} p\\ @@ -3089,12 +3006,12 @@ \subsubsection{Statement \label{subsec:Statement-filterable-recursive-type-1}\re \subparagraph{Proof} We follow the proof of Statement~\ref{subsec:functor-Statement-functor-recursive}. -Implement $\text{liftOpt}_{F}$ recursively as +Implement $\text{liftOpt}_{F}$ recursively as: \[ \text{liftOpt}_{F}(f^{:A\rightarrow\bbnum 1+B})\triangleq\text{liftOpt}_{S}(f)\bef\text{bimap}_{S}(\text{id})\big(\overline{\text{liftOpt}_{F}}(f)\big)=\text{liftOpt}_{S}(f)\bef\big(\overline{\text{liftOpt}_{F}}(f)\big)^{\uparrow S^{B,\bullet}}\quad. \] where $\text{liftOpt}_{S}$ is assumed to obey the laws. The lifting -to $F$ is defined (also recursively) by +to $F$ is defined (also recursively) by: \[ (f^{:A\rightarrow B})^{\uparrow F}\triangleq\text{bimap}_{S}(f)(\overline{f^{\uparrow F}})=f^{\uparrow S^{\bullet,R}}\bef\big(\overline{f^{\uparrow F}}\big)^{\uparrow S^{B,\bullet}}\quad. \] @@ -3127,8 +3044,8 @@ \subsubsection{Statement \label{subsec:Statement-filterable-recursive-type-1}\re S^{B,R^{\prime}}\ar[r]\sp(0.5){~\text{liftOpt}_{S}(g^{:B\rightarrow\bbnum 1+C})} & S^{C,R^{\prime}} } \] -We expect this naturality law to hold automatically for any fully -parametric implementation of $\text{liftOpt}_{S}$ (see Appendix~\ref{app:Proofs-of-naturality-parametricity}). +This naturality law will hold automatically for any fully parametric +implementation of $\text{liftOpt}_{S}$ (see Appendix~\ref{app:Proofs-of-naturality-parametricity}). \subsection{Filterable contrafunctors: motivation and examples} @@ -3167,7 +3084,7 @@ \subsection{Filterable contrafunctors: motivation and examples} instead of $Z$, since the type $\bbnum 1+Z$ has a default value $1+\bbnum 0^{:Z}$. The following two examples show how this works. -\subsubsection{Example \label{subsec:Example-first-filterable-contrafunctor}\ref{subsec:Example-first-filterable-contrafunctor}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-first-filterable-contrafunctor}\ref{subsec:Example-first-filterable-contrafunctor}\index{examples (with code)}} Implement \lstinline!filter! for the contrafunctor $C^{A}\triangleq A\rightarrow\bbnum 1+Z$, where $Z$ is a fixed type. @@ -3188,12 +3105,12 @@ \subsubsection{Example \label{subsec:Example-first-filterable-contrafunctor}\ref would lose information. If \lstinline!p(a) == true!, we may compute \lstinline!c(a)!, getting a value of type $\bbnum 1+Z$ that $d$ should return. If \lstinline!p(a) == false!, the function $d$ must -return \lstinline!None!. So, the code of $d$ must be +return \lstinline!None!. So, the code of $d$ must be: \begin{lstlisting} val d: A => Option[Z] = { a => if (p(a)) c(a) else None } \end{lstlisting} The transformation from \lstinline!c! to \lstinline!d! is a filtering -operation for the contrafunctor $C^{A}$, implemented as +operation for the contrafunctor $C^{A}$, implemented as: \begin{lstlisting} def filter[A](p: A => Boolean)(c: A => Option[Z]): A => Option[Z] = { a => if (p(a)) c(a) else None @@ -3210,13 +3127,12 @@ \subsubsection{Example \label{subsec:Example-first-filterable-contrafunctor-1}\r \subparagraph{Solution} -The code for the \lstinline!filter! function is +The code for the \lstinline!filter! function is: \begin{lstlisting} def filter[A](p: A => Boolean): ((Z, A => Z)) => (Z, A => Z) = { case (z, f) => (z, a => if (p(a)) f(a) else z) } \end{lstlisting} -\vspace{0.15\baselineskip} \[ \text{filt}_{C}(p^{:A\rightarrow\bbnum 2})\triangleq z^{:Z}\times f^{:A\rightarrow Z}\rightarrow z\times\bigg(a^{:A}\rightarrow p(a)\triangleright\,\begin{array}{|c||c|} & Z\\ @@ -3231,7 +3147,7 @@ \subsubsection{Example \label{subsec:Example-first-filterable-contrafunctor-1}\r Z\times\left(A\rightarrow Z\right)\cong\left(\bbnum 1\rightarrow Z\right)\times\left(A\rightarrow Z\right)\cong\bbnum 1+A\rightarrow Z\quad. \] For the contrafunctor $C^{A}\triangleq\bbnum 1+A\rightarrow Z$, the -\lstinline!filter! function is implemented by +\lstinline!filter! function is implemented by: \begin{lstlisting} def filter[A](p: A => Boolean)(c: Option[A] => Z): Option[A] => Z = { case Some(a) if p(a) => c(Some(a)) // Only apply `c` to `a` if `p(a) == true`. @@ -3253,7 +3169,7 @@ \subsubsection{Example \label{subsec:Example-first-filterable-contrafunctor-1}\r function, as long as suitable laws hold. To verify that equivalence, begin by defining the function \lstinline!inflate!, whose role is similar to that of \lstinline!deflate! for filterable functors. The -type signature of \lstinline!inflate! is +type signature of \lstinline!inflate! is: \[ \text{inflate}_{C}:C^{A}\rightarrow C^{\bbnum 1+A}\quad. \] @@ -3353,7 +3269,7 @@ \subsubsection{Example \label{subsec:filt-solved-example-5-1}\ref{subsec:filt-so with lifted functions. Typically, that is done by naturality laws. By analogy with Eq.~(\ref{eq:naturality-law-of-filter}) and making sure types match, we write a \textbf{naturality law} of\index{naturality law!of filter for contrafunctors@of \texttt{filter} for contrafunctors} -$\text{filt}_{C}$ as +$\text{filt}_{C}$ as: \begin{equation} \text{filt}_{C}(p^{:A\rightarrow\bbnum 2})\bef(f^{:B\rightarrow A})^{\downarrow C}=f^{\downarrow C}\bef\text{filt}_{C}(f\bef p)\quad.\label{eq:naturality-for-filter-for-contrafunctors} \end{equation} @@ -3364,7 +3280,7 @@ \subsubsection{Example \label{subsec:filt-solved-example-5-1}\ref{subsec:filt-so {\color{greenunder}\text{partial function law of }\text{filt}_{C}:}\quad & =\text{id}^{\downarrow C}\bef\text{filt}_{C}(p)=\text{filt}_{C}(p)\quad. \end{align*} Here we assumed the partial function law in the form similar to that -for functors, +for functors: \[ f_{|p}^{\downarrow C}\bef\text{filt}_{C}(p)=f^{\downarrow C}\bef\text{filt}_{C}(p)\quad. \] @@ -3380,23 +3296,15 @@ \subsubsection{Example \label{subsec:filt-solved-example-5-1}\ref{subsec:filt-so The calculation cannot proceed unless we can exchange lifted functions around \lstinline!inflate!. By analogy with Eq.~(\ref{eq:naturality-law-of-deflate}) and making changes suitable for contrafunctors, we obtain the \index{naturality law!of inflate for contrafunctors@of \texttt{inflate} for contrafunctors}\textbf{naturality -law} for the - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-2\baselineskip} +law} for the function $\text{inflate}_{C}$: +\begin{equation} +(f^{:B\rightarrow A})^{\downarrow C}\bef\text{inflate}_{C}=\text{inflate}_{C}\bef f^{\uparrow\text{Opt}\downarrow C}\quad.\label{eq:naturality-law-of-inflate-for-filterable-contrafunctor} +\end{equation} \[ \xymatrix{C^{A}\ar[r]\sp(0.5){\text{inflate}_{C}}\ar[d]\sb(0.45){(f^{:B\rightarrow A})^{\downarrow C}} & C^{\bbnum 1+A}\ar[d]\sp(0.45){(f^{:B\rightarrow A})^{\uparrow\text{Opt}\downarrow C}}\\ \xyScaleY{1.8pc}\xyScaleX{4.5pc}C^{B}\ar[r]\sp(0.5){\text{inflate}_{C}} & C^{\bbnum 1+B} } \] - -\vspace{-0.85\baselineskip} -\end{wrapfigure}% - -\noindent $\text{inflate}_{C}$ function: -\begin{equation} -(f^{:B\rightarrow A})^{\downarrow C}\bef\text{inflate}_{C}=\text{inflate}_{C}\bef f^{\uparrow\text{Opt}\downarrow C}\quad.\label{eq:naturality-law-of-inflate-for-filterable-contrafunctor} -\end{equation} With help of this law, we can finish the derivation: \begin{align*} {\color{greenunder}\text{use the naturality law}:}\quad & \gunderline{\text{get}^{\downarrow C}\bef\text{inflate}_{C}}\bef\psi_{\text{nonEmpty}}^{\downarrow C}=\text{inflate}_{C}\bef\gunderline{\text{get}^{\uparrow\text{Opt}\downarrow C}\bef\psi_{\text{nonEmpty}}^{\downarrow C}}\\ @@ -3516,7 +3424,7 @@ \subsubsection{Statement \label{subsec:Statement-function-type-exponential-filte \subparagraph{Proof} -We define the \lstinline!liftOpt! operation for $F^{\bullet}$ by +We define the \lstinline!liftOpt! operation for $F$ by: \begin{lstlisting} def liftOpt_F[A, B](f: A => Option[B])(p: G[B] => H[B]): G[A] => H[A] = { ga => liftOpt_H(f)(p(liftOpt_G(f)(ga))) } @@ -3555,7 +3463,7 @@ \subsubsection{Example \label{subsec:Example-search-functor}\ref{subsec:Example- is a function from the filterable contrafunctor $A\rightarrow\bbnum 1+Z$ to the filterable functor $\bbnum 1+A$. The simplest case of the search functor is found by setting $Z\triangleq\bbnum 1$, which gives -the type constructor +the type constructor: \[ S_{\bbnum 1}^{A}\triangleq(A\rightarrow\bbnum 2)\rightarrow\bbnum 1+A\quad. \] @@ -3638,20 +3546,13 @@ \subsubsection{Statement \label{subsec:Statement-recursive-filterable-contrafunc liftOpt_S(f)(sbfb).xmap_S(identity)(liftOpt_F(f)) )} \end{lstlisting} - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-1.9\baselineskip} \[ \xymatrix{S^{B,F^{B}}\ar[r]\sp(0.525){\text{liftOpt}_{S}(f^{:A\rightarrow\bbnum 1+B})}\ar[rd]\sb(0.45){\text{liftOpt}_{F}(f)\triangleq~~~} & S^{A,F^{B}}\ar[d]\sp(0.45){\big(\overline{\text{liftOpt}_{F}}(f)\big)^{\uparrow S^{A,\bullet}}}\\ \xyScaleY{1.8pc}\xyScaleX{6pc} & S^{A,F^{A}} } \] - -\vspace{-2.5\baselineskip} -\end{wrapfigure}% - -\noindent Note that $F^{B}\cong S^{B,F^{B}}$. As before, we use an -overline to mark recursive calls to the same function: +Note that $F^{B}\cong S^{B,F^{B}}$. As before, we use an overline +to mark recursive calls to the same function: \begin{align*} & \text{liftOpt}_{F}(f^{:A\rightarrow\bbnum 1+B})\triangleq\text{liftOpt}_{S}(f)\bef\big(\overline{\text{liftOpt}_{F}}(f)\big)^{\uparrow S^{A,\bullet}}\\ & =\text{liftOpt}_{S}(f)\bef\text{xmap}_{S}(\text{id})(\overline{\text{liftOpt}_{F}}(f))\quad. @@ -3667,7 +3568,7 @@ \subsubsection{Statement \label{subsec:Statement-recursive-filterable-contrafunc \] The naturality-identity law~(\ref{eq:naturality-identity-law-filterable-contrafunctor}) -is verified by +is verified by: \begin{align*} {\color{greenunder}\text{expect to equal }f^{\downarrow F}:}\quad & \text{liftOpt}_{F}(f\bef\text{pu}_{\text{Opt}})=\gunderline{\text{liftOpt}_{S}(f\bef\text{pu}_{\text{Opt}})}\bef\big(\overline{\text{liftOpt}_{F}}(f\bef\text{pu}_{\text{Opt}})\big)^{\uparrow S^{A,\bullet}}\\ {\color{greenunder}\text{law~(\ref{eq:naturality-identity-law-filterable-contrafunctor}) for }S^{\bullet,B}:}\quad & =f^{\downarrow S^{\bullet,B}}\bef\gunderline{\big(\overline{\text{liftOpt}_{F}}(f\bef\text{pu}_{\text{Opt}})\big)^{\uparrow S^{A,\bullet}}}\\ @@ -3683,19 +3584,11 @@ \subsubsection{Statement \label{subsec:Statement-recursive-filterable-contrafunc \end{align*} In this derivation, we have used the naturality law of $\text{liftOpt}_{S}$ with respect to lifting in the type parameter $A$ of $S^{A,R}$: - -\begin{wrapfigure}{l}{0.3\columnwidth}% -\vspace{-1.8\baselineskip} \[ \xymatrix{S^{B,R}\ar[r]\sp(0.5){\text{liftOpt}_{S}(f^{:A\rightarrow\bbnum 1+B})}\ar[d]\sp(0.4){(h^{:R\rightarrow R^{\prime}})^{\uparrow S^{B,\bullet}}} & S^{A,R}\ar[d]\sb(0.4){h^{\uparrow S^{A,\bullet}}}\\ \xyScaleY{1.8pc}\xyScaleX{6.0pc}S^{B,R^{\prime}}\ar[r]\sp(0.45){\text{liftOpt}_{S}(f)} & S^{A,R^{\prime}} } \] - -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.4\baselineskip} \begin{equation} \text{liftOpt}_{S}(f^{:A\rightarrow\bbnum 1+B})\bef(h^{:R\rightarrow R^{\prime}})^{\uparrow S^{A,\bullet}}=h^{\uparrow S^{B,\bullet}}\bef\text{liftOpt}_{S}(f)\quad.\label{eq:binaturality-law-of-filterable-profunctor} \end{equation} @@ -3733,7 +3626,7 @@ \section{Summary} decide whether those requirements can be implemented as a lawful \lstinline!filter! function. -\subsection{Solved examples\index{solved examples}} +\subsection{Examples\index{examples (with code)}} \subsubsection{Example \label{subsec:Example-filterable-laws-1}\ref{subsec:Example-filterable-laws-1}} @@ -3759,7 +3652,7 @@ \subsubsection{Example \label{subsec:Example-filterable-laws-1}\ref{subsec:Examp \subsubsection{Example \label{subsec:Example-filterable-laws-2}\ref{subsec:Example-filterable-laws-2}} -Use known filterable constructions to show that +Use known filterable constructions to show that: \[ F^{A}\triangleq\text{Int}\times\text{String}\rightarrow\bbnum 1+\text{Int}\times A+A\times\left(\bbnum 1+A\right)+(\text{Int}\rightarrow\bbnum 1+A+A\times A\times\text{String}) \] @@ -3770,12 +3663,12 @@ \subsubsection{Example \label{subsec:Example-filterable-laws-2}\ref{subsec:Examp We need to analyze the structure of the functor $F^{\bullet}$ to decide which constructions we may use. Define some auxiliary functors -that represents sub-expressions in $F^{A}$, +that represents sub-expressions in $F^{A}$: \begin{align*} R_{1}^{A}\triangleq\text{Int}\times\text{String}\rightarrow A\quad, & \quad\quad R_{2}^{A}\triangleq\text{Int}\rightarrow A\quad,\\ G^{A}\triangleq\bbnum 1+\text{Int}\times A+A\times\left(\bbnum 1+A\right)\quad, & \quad\quad H^{A}\triangleq\bbnum 1+A+A\times A\times\text{String}\quad. \end{align*} -Now we can rewrite the type $F^{A}$ as +Now we can rewrite the type $F^{A}$ as: \[ F^{A}=R_{1}^{L^{A}}\quad,\quad\quad L^{A}\triangleq G^{A}+R_{2}^{H^{A}}\quad. \] @@ -3833,12 +3726,12 @@ \subsubsection{Example \label{subsec:Example-identity-law-of-deflate}\ref{subsec \subparagraph{Solution} We may assume that $F$ satisfies the filtering laws. The function -\lstinline!inflate! can be equivalently written as +\lstinline!inflate! can be equivalently written as: \[ \text{inflate}_{F}=(x^{:A}\rightarrow\bbnum 0+x)^{\uparrow F}=\text{pu}_{\text{Opt}}^{\uparrow F}=\big(\psi_{(\_\rightarrow\text{true})}\big)^{\uparrow F}\quad. \] Now we can use the identity law~(\ref{eq:identity-law-of-filter}) -to derive +to derive: \[ \text{filt}_{F}(\_\rightarrow\text{true})=\psi_{(\_\rightarrow\text{true})}^{\uparrow F}\bef\text{deflate}_{F}=\text{inflate}_{F}\bef\text{deflate}_{F}\quad. \] @@ -3854,7 +3747,7 @@ \subsubsection{Example \label{subsec:Example-identity-law-of-deflate}\ref{subsec the \lstinline!Option! functor, $F^{A}\triangleq\bbnum 1+A=\text{Opt}^{A}$. With the equivalence $\text{Opt}^{\text{Opt}^{A}}\cong\bbnum 1+\bbnum 1+A$, we write the methods \lstinline!inflate! and \lstinline!deflate! -as +as: \begin{lstlisting} def inflate[A]: Option[A] => Option[Option[A]] = _.map(x => Some(x)) def deflate[A]: Option[Option[A]] => Option[A] = _.flatten @@ -3872,7 +3765,7 @@ \subsubsection{Example \label{subsec:Example-identity-law-of-deflate}\ref{subsec \end{array}\quad. \] The composition $\text{deflate}_{\text{Opt}}\bef\text{inflate}_{\text{Opt}}$ -is computed as +is computed as: \begin{align*} & \text{deflate}_{\text{Opt}}\bef\text{inflate}_{\text{Opt}}=\,\begin{array}{|c||cc|} & \bbnum 1 & A\\ @@ -3916,7 +3809,7 @@ \subsubsection{Example \label{subsec:Example-identity-law-of-deflate}\ref{subsec \bbnum 0 & 1 & \bbnum 0\end{array}\quad. \] In Scala code, this value $x$ is \lstinline!Some(None)!, so the -calculation corresponds to +calculation corresponds to: \begin{lstlisting} inflate(deflate(Some(None))) == None // Would have been Some(None) if the function were an identity. \end{lstlisting} @@ -3944,28 +3837,28 @@ \subsubsection{Example \label{subsec:Example-filterable-property-1+K}\ref{subsec (1+\bbnum 0^{:K^{A}})\triangleright\psi_{p}^{\uparrow H}\bef\text{deflate}_{H}\overset{?}{=}1+\bbnum 0^{:K^{A}}\quad. \] Any function lifted to $H^{\bullet}$ works separately for the two -parts of the disjunctive type $H^{A}=\bbnum 1+K^{A}$. So +parts of the disjunctive type $H^{A}=\bbnum 1+K^{A}$. So: \[ (1+\bbnum 0^{:K^{A}})\triangleright f^{\uparrow H}=1+\bbnum 0^{:K^{B}}\quad, \] regardless of the choice of $f^{:A\rightarrow B}$. Setting $f\triangleq\psi_{p}$, -we obtain +we obtain: \begin{equation} (1+\bbnum 0^{:K^{A}})\triangleright\psi_{p}^{\uparrow H}=1+\bbnum 0^{:K^{\bbnum 1+A}}\quad.\label{eq:emptyable-wrapper-psi-p-filter-derivation1} \end{equation} -It remains to show that +It remains to show that: \begin{equation} (1+\bbnum 0^{:K^{\bbnum 1+A}})\triangleright\text{deflate}_{H}\overset{?}{=}1+\bbnum 0^{:K^{A}}\quad.\label{eq:emptyable-wrapper-deflate-derivation1} \end{equation} We can proceed only if we use some law of \lstinline!deflate!; Example~\ref{subsec:Example-identity-law-of-deflate} shows a suitable law. Since Eq.~(\ref{eq:emptyable-wrapper-psi-p-filter-derivation1}) holds for all predicates $p$, we can choose the predicate $p$ to -be identically \lstinline!true! and get +be identically \lstinline!true! and get: \begin{equation} (1+\bbnum 0^{:K^{A}})\triangleright\psi_{(\_\rightarrow\text{true})}^{\uparrow H}=(1+\bbnum 0^{:K^{A}})\triangleright\text{inflate}_{H}=1+\bbnum 0^{:K^{\bbnum 1+A}}\quad.\label{eq:emptyable-wrapper-inflate-derivation1} \end{equation} Substituting this into Eq.~(\ref{eq:emptyable-wrapper-deflate-derivation1}), -we find +we find: \begin{align*} {\color{greenunder}\text{expect to equal }1+\bbnum 0^{:K^{A}}:}\quad & (\gunderline{1+\bbnum 0^{:K^{\bbnum 1+A}}})\triangleright\text{deflate}_{H}\\ {\color{greenunder}\text{use Eq.~(\ref{eq:emptyable-wrapper-inflate-derivation1})}:}\quad & =(1+\bbnum 0^{:K^{A}})\triangleright\gunderline{\text{inflate}_{H}\bef\text{deflate}_{H}}\\ @@ -3974,7 +3867,7 @@ \subsubsection{Example \label{subsec:Example-filterable-property-1+K}\ref{subsec This completes the proof of Eq.~(\ref{eq:emptyable-wrapper-deflate-derivation1}) and of the property~(\ref{eq:empty-filter-remains-empty-via-filt}). The same property can be expressed in terms of $\text{liftOpt}_{H}$ -as +as: \begin{equation} (1+\bbnum 0^{:K^{B}})\triangleright\text{liftOpt}_{H}(f^{:A\rightarrow\bbnum 1+B})=1+\bbnum 0^{:K^{B}}\quad.\label{eq:empty-filter-remains-empty-via-liftOpt} \end{equation} @@ -3990,12 +3883,12 @@ \subsubsection{Example \label{subsec:Example-filterable-laws-3-1}\ref{subsec:Exa \subparagraph{Solution} We need to define $\text{liftOpt}_{F}$ and verify its laws, assuming -that $\text{liftOpt}_{G}$ and $\text{liftOpt}_{H}$, +that $\text{liftOpt}_{G}$ and $\text{liftOpt}_{H}$ with types: \[ \text{liftOpt}_{G}(f^{:A\rightarrow\bbnum 1+B}):G^{A}\rightarrow G^{B}\quad,\quad\quad\text{liftOpt}_{H}(f^{:A\rightarrow\bbnum 1+B}):\bbnum 1+K^{A}\rightarrow\bbnum 1+K^{B}\quad, \] are already available and obey the same laws. We need to implement -the type signature +the type signature: \[ \text{liftOpt}_{F}(f^{:A\rightarrow\bbnum 1+B}):G^{K^{A}}\rightarrow G^{K^{B}}\quad. \] @@ -4040,7 +3933,7 @@ \subsubsection{Example \label{subsec:Example-filterable-laws-3-1}\ref{subsec:Exa This simplification depends on the property~(\ref{eq:empty-filter-remains-empty-via-liftOpt}) of $\text{liftOpt}_{H}$ shown in Example~\ref{subsec:Example-filterable-property-1+K} and does \emph{not} work for arbitrary functions $p$, $q$ having -the same type signatures as in Eq.~(\ref{eq:simplify-kleisli-opt-right-pure}), +the same type signatures as in Eq.~(\ref{eq:simplify-kleisli-opt-right-pure}): \[ p^{:A\rightarrow\bbnum 1+B}\diamond_{_{\text{Opt}}}(\text{pu}_{\text{Opt}}^{:B\rightarrow\bbnum 1+B}\bef q^{:\bbnum 1+B\rightarrow\bbnum 1+C})\neq p\bef q\quad. \] @@ -4101,7 +3994,7 @@ \subsubsection{Example \label{subsec:Example-filterable-laws-unrolling-trick}\re & \cong\bbnum 1+A+A\times A+A\times A\times A+...+\underbrace{A\times...\times A}_{n\text{ times}}\times\,\text{List}^{A}\quad, \end{align*} we find that the definition~(\ref{eq:def-recursive-functor-unrolling}) -of $F^{A}$ can be \textsf{``}unrolled\textsf{''} $n$ times as +of $F^{A}$ can be \textsf{``}unrolled\textsf{''} $n$ times as: \begin{align*} F^{A} & \triangleq P^{A}+Q^{A}\times F^{A}\cong P^{A}+Q^{A}\times(P^{A}+Q^{A}\times(P^{A}+...(P^{A}+Q^{A}\times F^{A})))\\ & \cong P^{A}+Q^{A}\times P^{A}+Q^{A}\times Q^{A}\times P^{A}+...+\underbrace{Q^{A}\times...\times Q^{A}}_{n\text{ times}}\times\,F^{A}\\ @@ -4115,7 +4008,7 @@ \subsubsection{Example \label{subsec:Example-filterable-laws-unrolling-trick}\re $Q^{A}\triangleq A\times\left(\bbnum 1+A\right)$ is \emph{not} filterable (we saw that in Section~\ref{subsec:Examples-of-non-filterable-functors}). However, we can derive from Statement~\ref{subsec:Statement-filterable-coproduct-1}, -setting $G^{A}\triangleq\bbnum 1+A$, that +setting $G^{A}\triangleq\bbnum 1+A$, that: \[ H^{A}\triangleq\bbnum 1+A\times\left(\bbnum 1+A\right)=\bbnum 1+Q^{A} \] @@ -4130,7 +4023,8 @@ \subsubsection{Example \label{subsec:Example-filterable-laws-4}\ref{subsec:Examp \subparagraph{Solution} -Try to implement the function $\text{inflate}_{C}:C^{A}\rightarrow C^{\bbnum 1+A}$, +Try to implement the function $\text{inflate}_{C}:C^{A}\rightarrow C^{\bbnum 1+A}$ +and write: \[ \text{inflate}_{C}:(A\rightarrow Z)\rightarrow\bbnum 1+A\rightarrow Z\quad,\quad\quad\text{inflate}_{C}=c^{:A\rightarrow Z}\rightarrow p^{:\bbnum 1+A}\rightarrow\text{???}^{:Z}\quad. \] @@ -4255,7 +4149,7 @@ \subsection{Naturality laws and natural transformations\label{subsec:Naturality- to move $\psi_{p}^{\downarrow C}$ to the left of $\text{filt}_{C}$ in the expression $\text{get}^{\downarrow C}\bef\text{filt}_{C}(\text{nonEmpty})\bef\psi_{p}^{\downarrow C}$, or else we could make no progress with the calculations. The required -interchange was possible by using the law~(\ref{eq:naturality-for-filter-for-contrafunctors}), +interchange was possible due to the law~(\ref{eq:naturality-for-filter-for-contrafunctors}): \[ \text{filt}_{C}(p)\bef f^{\downarrow C}=f^{\downarrow C}\bef\text{filt}_{C}(f\bef p)\quad. \] @@ -4287,20 +4181,20 @@ \subsection{Naturality laws and natural transformations\label{subsec:Naturality- \textbf{\small{}Pattern} & \textbf{\small{}Type signature} & \textbf{\small{}Naturality law}\tabularnewline \hline \hline -{\small{}natural transformation} & {\small{}$t^{A}:F^{A}\rightarrow G^{A}$} & {\small{}$(f^{:A\rightarrow B})^{\uparrow F}\bef t^{B}=t^{A}\bef f^{\uparrow G}$}\tabularnewline +{\small{}2-transformation} & {\small{}$t^{A}:F^{A}\rightarrow G^{A}$} & {\small{}$(f^{:A\rightarrow B})^{\uparrow F}\bef t^{B}=t^{A}\bef f^{\uparrow G}$}\tabularnewline \hline -{\small{}parameterized transformation} & {\small{}$t^{A}:C^{A}\rightarrow F^{A}\rightarrow G^{A}$} & {\small{}$f^{\uparrow F}\bef t^{B}(c)=t(c\triangleright f^{\downarrow C})\bef f^{\uparrow G}$}\tabularnewline +{\small{}3-transformation} & {\small{}$t^{A}:C^{A}\rightarrow F^{A}\rightarrow G^{A}$} & {\small{}$f^{\uparrow F}\bef t^{B}(c)=t(c\triangleright f^{\downarrow C})\bef f^{\uparrow G}$}\tabularnewline \hline -{\small{}generalized lifting; $A$-naturality } & {\small{}$t^{A,B}:(A\rightarrow G^{B})\rightarrow F^{A}\rightarrow F^{B}$} & {\small{}$(f^{:A\rightarrow B})^{\uparrow F}\bef t^{B,C}(p)=t^{A,C}(f\bef p)$}\tabularnewline +{\small{}$A$-lifting} & {\small{}$t^{A,B}:(A\rightarrow G^{B})\rightarrow F^{A}\rightarrow F^{B}$} & {\small{}$(f^{:A\rightarrow B})^{\uparrow F}\bef t^{B,C}(p)=t^{A,C}(f\bef p)$}\tabularnewline \hline -{\small{}generalized lifting; $B$-naturality} & {\small{}$t^{A,B}:(A\rightarrow G^{B})\rightarrow F^{A}\rightarrow F^{B}$} & {\small{}$t^{A,B}(p)\bef(g^{:B\rightarrow C})^{\uparrow F}=t^{A,C}(p\bef g^{\uparrow G})$}\tabularnewline +{\small{}$B$-lifting} & {\small{}$t^{A,B}:(A\rightarrow G^{B})\rightarrow F^{A}\rightarrow F^{B}$} & {\small{}$t^{A,B}(p)\bef(g^{:B\rightarrow C})^{\uparrow F}=t^{A,C}(p\bef g^{\uparrow G})$}\tabularnewline \hline \end{tabular} \par\end{center} Let us now look at each of these patterns in detail. -\paragraph{Natural transformations} +\paragraph{\textquotedblleft 2-transformation\textquotedblright} This pattern covers functions with type signatures of the form $F^{A}\rightarrow G^{A}$, where $F$ and $G$ must be both functors or both contrafunctors. @@ -4311,7 +4205,7 @@ \subsection{Naturality laws and natural transformations\label{subsec:Naturality- of type $A$ from one \textsf{``}wrapper\textsf{''} to another and may rearrange that data in a way that does not depend on the type $A$. An example is the \lstinline!headOption! method defined in Scala on various sequence -types such as \lstinline!List!, +types such as \lstinline!List!: \[ \text{headOpt}:\text{List}^{A}\rightarrow\text{Opt}^{A}\quad. \] @@ -4330,20 +4224,13 @@ \subsection{Naturality laws and natural transformations\label{subsec:Naturality- and then transform the data with a lifted function $f$ (and we will then need to lift $f$ to the \lstinline!Option! functor rather than to the \lstinline!List! functor); the results will be equal. We write -this requirement as an equation called the naturality law of \lstinline!headOption!: - -\begin{wrapfigure}{l}{0.25\columnwidth}% -\vspace{-2.1\baselineskip} +this requirement as an equation called the \textbf{naturality law}\index{naturality law!of headOption@of \texttt{headOption}} +of \lstinline!headOption!: \[ \xymatrix{\text{List}^{A}\ar[r]\sp(0.55){\text{headOpt}^{A}}\ar[d]\sp(0.4){(f^{:A\rightarrow B})^{\uparrow\text{List}}} & \text{Opt}^{A}\ar[d]\sb(0.4){f^{\uparrow\text{Opt}}}\\ \xyScaleY{1.6pc}\xyScaleX{4.5pc}\text{List}^{B}\ar[r]\sp(0.55){\text{headOpt}^{B}} & \text{Opt}^{B} } \] - -\vspace{-0.5\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.5\baselineskip} \[ (f^{:A\rightarrow B})^{\uparrow\text{List}}\bef\text{headOpt}^{B}=\text{headOpt}^{A}\bef f^{\uparrow\text{Opt}}\quad. \] @@ -4361,19 +4248,11 @@ \subsection{Naturality laws and natural transformations\label{subsec:Naturality- we have already seen. All these naturality laws are captured by the \textsf{``}natural transformation\textsf{''} pattern, which we can formulate for arbitrary functors $F^{\bullet}$ and $G^{\bullet}$ as the following law: - -\begin{wrapfigure}{l}{0.2\columnwidth}% -\vspace{-2.2\baselineskip} \[ \xymatrix{F^{A}\ar[r]\sp(0.55){t^{A}}\ar[d]\sp(0.4){(f^{:A\rightarrow B})^{\uparrow F}} & G^{A}\ar[d]\sp(0.4){f^{\uparrow G}}\\ \xyScaleY{1.7pc}\xyScaleX{3.5pc}F^{B}\ar[r]\sp(0.55){t^{B}} & G^{B} } \] - -\vspace{-0.5\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.3\baselineskip} \begin{equation} (f^{:A\rightarrow B})^{\uparrow F}\bef t^{:F^{B}\rightarrow G^{B}}=t^{:F^{A}\rightarrow G^{A}}\bef f^{\uparrow G}\quad.\label{eq:law-natural-transformation-of-functors} \end{equation} @@ -4389,19 +4268,11 @@ \subsection{Naturality laws and natural transformations\label{subsec:Naturality- The analogous naturality law for natural transformations $t:C^{A}\rightarrow D^{A}$ between contrafunctors $C$ and $D$ has exactly the same form, but the order of type parameters must be swapped: - -\begin{wrapfigure}{l}{0.2\columnwidth}% -\vspace{-2.2\baselineskip} \[ \xymatrix{C^{A}\ar[r]\sp(0.55){t^{A}}\ar[d]\sp(0.4){(f^{:B\rightarrow A})^{\downarrow C}} & D^{A}\ar[d]\sp(0.4){f^{\downarrow D}}\\ \xyScaleY{1.7pc}\xyScaleX{3.5pc}C^{B}\ar[r]\sp(0.55){t^{B}} & D^{B} } \] - -\vspace{-0.5\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.2\baselineskip} \begin{equation} (f^{:B\rightarrow A})^{\downarrow C}\bef t^{:C^{B}\rightarrow D^{B}}=t^{:C^{A}\rightarrow D^{A}}\bef f^{\downarrow D}\quad.\label{eq:law-natural-transformation-of-contrafunctors} \end{equation} @@ -4410,26 +4281,25 @@ \subsection{Naturality laws and natural transformations\label{subsec:Naturality- and the naturality law~(\ref{eq:naturality-law-of-inflate-for-filterable-contrafunctor}) for $\text{inflate}_{C}$. -\paragraph{Parameterized transformations} +\paragraph{\textquotedblleft 3-transformation\textquotedblright} -The second pattern, which may be called \textsf{``}parameterized transformation\textsf{''}, -is a curried function that takes a first argument of type $C^{A}$ -and returns a natural transformation of type $F^{A}\rightarrow G^{A}$. -This pattern is recognized by a type signature $C^{A}\rightarrow F^{A}\rightarrow G^{A}$ +The second pattern, which we called \textsf{``}3-transformation\textsf{''}, is a curried +function that takes a first argument of type $C^{A}$ and returns +a natural transformation of type $F^{A}\rightarrow G^{A}$. This pattern +is recognized by a type signature $C^{A}\rightarrow F^{A}\rightarrow G^{A}$ where $C$ is a contrafunctor while $F$ and $G$ are functors (or vice versa, $C$ is a functor while $F$ and $G$ are contrafunctors). -Examples of parameterized transformations are the methods \lstinline!filter!, +Examples of such type signatures are the methods \lstinline!filter!, \lstinline!takeWhile!, and \lstinline!find!, defined in the Scala library for sequence-like type constructors (e.g., \lstinline!List!, \lstinline!Vector!, or \lstinline!Array!). For functions of this kind, naturality laws must modify the argument of type $C^{A}$ when changing the order of lifted functions. In order -to formulate the naturality law for a parameterized transformation -$t^{A}:C^{A}\rightarrow F^{A}\rightarrow G^{A}$, use an arbitrary -function $f^{:A\rightarrow B}$ and first transform the argument of -type $F^{A}$ into $F^{B}$ using $f^{\uparrow F}$ before applying -$t^{B}(c)$: +to formulate the naturality law for $t^{A}:C^{A}\rightarrow F^{A}\rightarrow G^{A}$, +use an arbitrary function $f^{:A\rightarrow B}$ and first transform +the argument of type $F^{A}$ into $F^{B}$ using $f^{\uparrow F}$ +before applying $t^{B}(c)$: \[ f^{\uparrow F}\bef t^{B}(c^{:C^{B}})=\text{???}^{:F^{A}\rightarrow G^{B}}\quad. \] @@ -4440,20 +4310,20 @@ \subsection{Naturality laws and natural transformations\label{subsec:Naturality- f^{\uparrow F}\bef t^{B}(c^{:C^{B}})=t^{A}(\text{???}^{:C^{A}})\bef f^{\uparrow G}\quad. \] The argument of $t$ of type $C^{A}$ is obtained by applying $f^{\downarrow C}$ -to $c$. So, the law is +to $c$. So, the law is: \begin{equation} f^{\uparrow F}\bef t(c)=t(c\triangleright f^{\downarrow C})\bef f^{\uparrow G}\quad.\label{eq:naturality-law-general-parameterized-transformation} \end{equation} A similar law can be derived for the case when $C^{\bullet}$ is a functor and $F^{\bullet},G^{\bullet}$ are contrafunctors. -Parameterized transformations $t:C^{A}\rightarrow F^{A}\rightarrow G^{A}$ -are reduced to natural transformations if we swap the order of curried -arguments to $F^{A}\rightarrow C^{A}\rightarrow G^{A}$ and define -the functor $H^{A}\triangleq C^{A}\rightarrow G^{A}$. Then $F^{A}\rightarrow C^{A}\rightarrow G^{A}=F^{A}\rightarrow H^{A}$, +Functions $t:C^{A}\rightarrow F^{A}\rightarrow G^{A}$ are reduced +to natural transformations if we swap the order of curried arguments +to $F^{A}\rightarrow C^{A}\rightarrow G^{A}$ and define the functor +$H^{A}\triangleq C^{A}\rightarrow G^{A}$. Then $F^{A}\rightarrow C^{A}\rightarrow G^{A}=F^{A}\rightarrow H^{A}$, which is a type signature of a natural transformation. Denoting by $\tilde{t}:F^{A}\rightarrow H^{A}$ the function $t$ with its arguments -swapped, we can write the naturality law of $\tilde{t}$ as +swapped, we can write the naturality law of $\tilde{t}$ as: \[ f^{\uparrow F}\bef\tilde{t}=\tilde{t}\bef f^{\uparrow H}\quad,\quad\text{where}\quad\tilde{t}\triangleq p^{:F^{A}}\rightarrow c^{:C^{A}}\rightarrow p\triangleright t(c)\quad. \] @@ -4481,7 +4351,7 @@ \subsection{Naturality laws and natural transformations\label{subsec:Naturality- naturality law of $t:C^{A}\rightarrow F^{A}\rightarrow G^{A}$ can then be derived from $t$\textsf{'}s type signature. -\paragraph{Liftings} +\paragraph{\textquotedblleft Liftings\textquotedblright} A \textsf{``}lifted\textsf{''} function $f^{\uparrow F}$ is the result of applying a functor $F$\textsf{'}s \lstinline!map! operation to a function $f$. The @@ -4494,7 +4364,7 @@ \subsection{Naturality laws and natural transformations\label{subsec:Naturality- & \text{liftOpt}_{F}:(A\rightarrow\text{Opt}^{B})\rightarrow F^{A}\rightarrow F^{B}\quad. \end{align*} Replacing $\text{Opt}$ by an arbitrary functor $G$, we obtain the -type signature of a \textsf{``}generalized lifting\textsf{''}, +type signature of a \textsf{``}generalized lifting\textsf{''}: \[ \text{lift}_{G,F}^{A,B}:(A\rightarrow G^{B})\rightarrow F^{A}\rightarrow F^{B}\quad, \] @@ -4635,13 +4505,13 @@ \subsubsection{Statement \label{subsec:Statement-Kleisli-Option-laws}\ref{subsec did not look at the appropriate level of abstraction to see that. While programmers may accept the work of writing these proofs twice, a mathematician would prefer to define a \textsf{``}generalized lifting\textsf{''} -that replaces $\text{Opt}$ by a functor $M$, +that replaces $\text{Opt}$ by a functor $M$: \begin{align*} & \text{lift}_{M,F}:(A\rightarrow M^{B})\rightarrow F^{A}\rightarrow F^{B}\quad,\\ & \text{pu}_{M}:A\rightarrow M^{A}\quad,\quad\quad\diamond_{_{M}}:(A\rightarrow M^{B})\rightarrow(B\rightarrow M^{C})\rightarrow(A\rightarrow M^{C})\quad, \end{align*} and postulating the required properties as the set of identity, associativity, -and composition laws, +and composition laws: \begin{align*} & \text{lift}_{M,F}(\text{pu}_{M}^{:A\rightarrow M^{A}})=\text{id}^{:F^{A}\rightarrow F^{A}}\quad,\quad\quad\text{lift}_{M,F}(f)\bef\text{lift}_{M,F}(g)=\text{lift}_{M,F}(f\diamond_{_{M}}g)\quad,\\ & \text{pu}_{M}\diamond_{_{M}}g=g\quad,\quad\quad f\diamond_{_{M}}\text{pu}_{M}=f\quad,\quad\quad\big(f\diamond_{_{M}}g\big)\diamond_{_{M}}h=f\diamond_{_{M}}\big(g\diamond_{_{M}}h\big)\quad. @@ -4691,20 +4561,20 @@ \subsection{Motivation for using category theory\label{subsec:Motivation-for-usi must be chosen appropriately. These choices, together with a definition of the admissible types $A,B,...$, define a \textbf{category}, e.g.: \begin{center} -\begin{tabular}{|c|c|c|c|c|} +\begin{tabular}{|c|c|c|c|} \hline -\textbf{\small{}Category} & \textbf{\small{}Types $A,B,...$} & \textbf{\small{}Morphisms} $f:A\leadsto B$ & \textbf{\small{}Identity morphism} & \textbf{\small{}Composition}\tabularnewline +\textbf{\small{}Category} & \textbf{\small{}Morphisms} $f:A\leadsto B$ & \textbf{\small{}Identity morphism} & \textbf{\small{}Composition}\tabularnewline \hline \hline -{\small{}\textsf{``}plain\textsf{''}} & {\small{}$\text{Int},\text{String},...$} & {\small{}$f:A\rightarrow B$} & {\small{}$\text{id}^{:A\rightarrow A}$} & {\small{}$f\bef g$}\tabularnewline +{\small{}\textsf{``}plain\textsf{''}} & {\small{}$f:A\rightarrow B$} & {\small{}$\text{id}^{:A\rightarrow A}$} & {\small{}$f\bef g$}\tabularnewline \hline -{\small{}\textsf{``}reversed\textsf{''}} & {\small{}$\text{Int},\text{String},...$} & {\small{}$f:B\rightarrow A$} & {\small{}$\text{id}^{:A\rightarrow A}$} & {\small{}$g\bef f$}\tabularnewline +{\small{}\textsf{``}reversed\textsf{''}} & {\small{}$f:B\rightarrow A$} & {\small{}$\text{id}^{:A\rightarrow A}$} & {\small{}$g\bef f$}\tabularnewline \hline -{\small{}\textsf{``}$M$-Kleisli\textsf{''}} & {\small{}$\text{Int},\text{String},...$} & {\small{}$f:A\rightarrow M^{B}$} & {\small{}$\text{pu}_{M}:A\rightarrow M^{A}$} & {\small{}$f\diamond_{_{M}}g$}\tabularnewline +{\small{}\textsf{``}$M$-Kleisli\textsf{''}} & {\small{}$f:A\rightarrow M^{B}$} & {\small{}$\text{pu}_{M}:A\rightarrow M^{A}$} & {\small{}$f\diamond_{_{M}}g$}\tabularnewline \hline -{\small{}\textsf{``}reversed $M$-Kleisli\textsf{''}} & {\small{}$\text{Int},\text{String},...$} & {\small{}$f:B\rightarrow M^{A}$} & {\small{}$\text{pu}_{M}:A\rightarrow M^{A}$} & {\small{}$g\diamond_{_{M}}f$}\tabularnewline +{\small{}\textsf{``}reversed $M$-Kleisli\textsf{''}} & {\small{}$f:B\rightarrow M^{A}$} & {\small{}$\text{pu}_{M}:A\rightarrow M^{A}$} & {\small{}$g\diamond_{_{M}}f$}\tabularnewline \hline -{\small{}\textsf{``}$F$-lifted\textsf{''}} & {\small{}$F^{\text{Int}},F^{\text{String}},...$} & {\small{}$f:F^{A}\rightarrow F^{B}$} & {\small{}$\text{id}^{:F^{A}\rightarrow F^{A}}$} & {\small{}$f\bef g$}\tabularnewline +{\small{}\textsf{``}$F$-lifted\textsf{''}} & {\small{}$f:F^{A}\rightarrow F^{B}$} & {\small{}$\text{id}^{:F^{A}\rightarrow F^{A}}$} & {\small{}$f\bef g$}\tabularnewline \hline \end{tabular} \par\end{center} diff --git a/sofp-src/sofp-free-type.lyx b/sofp-src/sofp-free-type.lyx index 4c2e99b4e..093b1dfaf 100644 --- a/sofp-src/sofp-free-type.lyx +++ b/sofp-src/sofp-free-type.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,16 +178,26 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} -\newcommand{\shui}{\begin{CJK}{UTF8}{gbsn}水\end{CJK}} -\usepackage{CJKutf8} % For occasional Chinese characters. Also, add "russian" to documentclass. + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false \language english \language_package default -\inputencoding utf8 +\inputencoding auto \fontencoding global \font_roman "palatino" "default" \font_sans "helvet" "default" @@ -248,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -11465,7 +11478,7 @@ We will use the existential quantifier notation for proofs of properties \end_layout \begin_layout Subsection -Expressing types with existential quantifiers via universal quantifiers +Expressing existential quantifiers via universal quantifiers \end_layout \begin_layout Standard @@ -24896,7 +24909,7 @@ Let \begin_inset Formula $P$ \end_inset - be a polynomial functor and let + be a traversable functor and let \begin_inset Formula $E$ \end_inset @@ -24921,7 +24934,7 @@ Let \begin_inset Formula $p_{E}^{T}:P^{E^{T}}\rightarrow E^{T}$ \end_inset -, natural in + natural in \begin_inset Formula $T$ \end_inset @@ -25039,7 +25052,19 @@ injective function no other \emph default laws. - To formulate this requirement precisely, we use law expressions + ***This is misleading, as in fact +\begin_inset Formula $E^{T}$ +\end_inset + + does satisfy more laws for some +\begin_inset Formula $T$ +\end_inset + +; say for +\begin_inset Formula $T=\bbnum 0$ +\end_inset + +.***To formulate this requirement precisely, we use law expressions \begin_inset Formula $e:\text{FPR}^{\text{Int}}\times\text{FPR}^{\text{Int}}$ \end_inset @@ -25595,11 +25620,7 @@ p_{E^{C}}\bef\text{in}_{E}^{C}\bef\text{eval}_{\text{FPR}}^{C}\overset{?}{=}\big \end_inset - -\end_layout - -\begin_layout Standard -***Verifying Eq. +Verifying Eq. \begin_inset space ~ \end_inset @@ -25613,7 +25634,7 @@ noprefix "false" \end_inset -) takes much more work. +) takes more work. We first note that for any \begin_inset Formula $C$ \end_inset @@ -33649,7 +33670,7 @@ L^{A}\cong\bbnum 1+(\bbnum 1\rightarrow A\times L^{A})\quad.\label{eq:fixpoint-t \end_inset -We may also write the same equation using a suitable structure functor +We may write the same equation using a recursion scheme \begin_inset Formula $F$ \end_inset @@ -37021,7 +37042,7 @@ Reduced encoding requires proofs and more complex operations \end_layout \begin_layout Subsection -Beyond Yoneda: using parametricity to simplify quantified types +Beyond Yoneda: simplifying quantified types \end_layout \begin_layout Standard @@ -37060,7 +37081,7 @@ noprefix "false" \end_layout \begin_layout Standard -For any exponential-polynomial functor +For any polynomial functor \begin_inset Formula $F$ \end_inset @@ -37077,11 +37098,7 @@ For any exponential-polynomial functor \end_inset are equivalent when restricted to fully parametric implementations. - The same holds when -\begin_inset Formula $F$ -\end_inset - - is an exponential-polynomial contrafunctor. + \end_layout \begin_layout Subparagraph @@ -37131,6 +37148,8 @@ noprefix "false" \end_inset are equivalent when restricted to fully parametric implementations. + *** I already talked about this earlier in this chapter. + Move proof here? \end_layout \begin_layout Standard diff --git a/sofp-src/sofp-free-type.tex b/sofp-src/sofp-free-type.tex index f9c3081d2..1078ce99d 100644 --- a/sofp-src/sofp-free-type.tex +++ b/sofp-src/sofp-free-type.tex @@ -37,30 +37,30 @@ \section{Motivation for the free monad: create a DSL in five stages} \subsection{Stage 1: unevaluated expression trees} The first example is a DSL for reading and writing files at given -file paths. Direct Scala code for such operations may look like this:\inputencoding{latin9} +file paths. Direct Scala code for such operations may look like this: \begin{lstlisting} import java.nio.file.{Files, Paths} val p = Paths.get("config_location.txt") val configLocation = Paths.get(new String(Files.readAllBytes(p))) val config = new String(Files.readAllBytes(configLocation)) \end{lstlisting} -\inputencoding{utf8}The DSL will represent these operations by the case classes \inputencoding{latin9}\lstinline!Val!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!Path!\inputencoding{utf8}, and \inputencoding{latin9}\lstinline!Read!\inputencoding{utf8}:\inputencoding{latin9} +The DSL will represent these operations by the case classes \lstinline!Val!, +\lstinline!Path!, and \lstinline!Read!: \begin{lstlisting} sealed trait PrgFile final case class Val(s: String) extends PrgFile final case class Path(p: PrgFile) extends PrgFile final case class Read(p: PrgFile) extends PrgFile \end{lstlisting} -\inputencoding{utf8}A DSL program of type \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} -is a value consisting of many nested case classes:\inputencoding{latin9} +A DSL program of type \lstinline!PrgFile! is a value consisting of +many nested case classes: \begin{lstlisting} val prgFile: PrgFile = Read(Path(Read(Path(Val("config_location.txt"))))) \end{lstlisting} -\inputencoding{utf8}Such values represent an \emph{unevaluated} expression tree corresponding +Such values represent an \emph{unevaluated} expression tree corresponding to the operations that need to be performed. To actually perform those -operations and extract the final \inputencoding{latin9}\lstinline!String!\inputencoding{utf8} -value, we use a \textsf{``}runner\textsf{''} function:\index{runner!for free monads}\inputencoding{latin9} +operations and extract the final \lstinline!String! value, we use +a \textsf{``}runner\textsf{''} function:\index{runner!for free monads} \begin{lstlisting} def runFile: PrgFile => String = { case Val(s) => s @@ -69,19 +69,18 @@ \subsection{Stage 1: unevaluated expression trees} case x => throw new Exception(s"Illegal PrgFile operation: $x") } \end{lstlisting} -\inputencoding{utf8}To test this code, we prepare a file \inputencoding{latin9}\lstinline!config.txt!\inputencoding{utf8} -containing the text \inputencoding{latin9}\lstinline!version = 1!\inputencoding{utf8} -and a file \inputencoding{latin9}\lstinline!config_location.txt!\inputencoding{utf8} -containing the text \inputencoding{latin9}\lstinline!config.txt!\inputencoding{utf8}. -Then we can run the DSL program and get the result:\inputencoding{latin9} +To test this code, we prepare a file \lstinline!config.txt! containing +the text \lstinline!version = 1! and a file \lstinline!config_location.txt! +containing the text \lstinline!config.txt!. Then we can run the DSL +program and get the result: \begin{lstlisting} scala> runFile(prgFile) res2: String = "version = 1" \end{lstlisting} -\inputencoding{utf8} + The second example is a DSL for calculations with complex numbers. -Begin by implementing a data structure (\inputencoding{latin9}\lstinline!case class Complex!\inputencoding{utf8}) -with some operations: \inputencoding{latin9} +Begin by implementing a data structure (\lstinline!case class Complex!) +with some operations: \begin{lstlisting}[mathescape=true] final case class Complex(x: Double, y: Double) { def +(other: Complex): Complex = Complex(x + other.x, y + other.y) @@ -101,12 +100,12 @@ \subsection{Stage 1: unevaluated expression trees} scala> c.rotate(Complex(0, 1).phase) // Multiply by $\color{dkgreen}i$ res1: Complex = Complex(2.000000000000001, 11.0) \end{lstlisting} -\inputencoding{utf8} + Instead of running a complex-number program directly, the DSL pattern first creates a data structure that contains an unevaluated expression tree describing what needs to be computed. Separate case classes are -used for different operations as well as for inserting a literal \inputencoding{latin9}\lstinline!Complex!\inputencoding{utf8} -value (\inputencoding{latin9}\lstinline!Val!\inputencoding{utf8}):\inputencoding{latin9} +used for different operations as well as for inserting a literal \lstinline!Complex! +value (\lstinline!Val!): \begin{lstlisting} sealed trait PrgComplex final case class Val(c: Complex) extends PrgComplex @@ -116,14 +115,14 @@ \subsection{Stage 1: unevaluated expression trees} final case class Phase(p: PrgComplex) extends PrgComplex final case class Rotate(p: PrgComplex, a: PrgComplex) extends PrgComplex \end{lstlisting} -\inputencoding{utf8} -Complex-number calculations are now represented by nested case classes:\inputencoding{latin9} + +Complex-number calculations are now represented by nested case classes: \begin{lstlisting} val prgComplex1: PrgComplex = Conj(Mul(Val(Complex(1, 2)), Val(Complex(3, -4)))) val prgComplex2: PrgComplex = Rotate(prgComplex1, Phase(Val(Complex(0, 1)))) \end{lstlisting} -\inputencoding{utf8}A simple runner for the DSL programs of type \inputencoding{latin9}\lstinline!PrgComplex!\inputencoding{utf8} -can be written like this:\inputencoding{latin9} +A simple runner for the DSL programs of type \lstinline!PrgComplex! +can be written like this: \begin{lstlisting} def runComplex: PrgComplex => Complex = { case Val(c) => c @@ -134,9 +133,8 @@ \subsection{Stage 1: unevaluated expression trees} case Rotate(p, alpha) => runComplex(p).rotate(runComplex(alpha).x) // Here alpha must be a phase. } \end{lstlisting} -\inputencoding{utf8}We can now apply \inputencoding{latin9}\lstinline!runComplex!\inputencoding{utf8} -to some DSL programs and get the results as \inputencoding{latin9}\lstinline!Complex!\inputencoding{utf8} -values:\inputencoding{latin9} +We can now apply \lstinline!runComplex! to some DSL programs and +get the results as \lstinline!Complex! values: \begin{lstlisting} scala> runComplex(prgComplex1) res2: Complex = Complex(11.0, -2.0) @@ -144,10 +142,10 @@ \subsection{Stage 1: unevaluated expression trees} scala> runComplex(prgComplex2) res3: Complex = Complex(2.000000000000001, 11.0) \end{lstlisting} -\inputencoding{utf8} + Although the DSLs are simple, we already get several benefits. By -representing file operations as values of type \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} -and complex-number calculations as values of type \inputencoding{latin9}\lstinline!PrgComplex!\inputencoding{utf8}, +representing file operations as values of type \lstinline!PrgFile! +and complex-number calculations as values of type \lstinline!PrgComplex!, we can compose, verify, or optimize our DSL programs \emph{before} running them. We may also implement different runners that execute the same DSL program on a remote computer, on a distributed file system, @@ -158,31 +156,26 @@ \subsection{Stage 2: implementing type safety in the DSLs} The two DSLs defined in the previous section are not fully type-checked\index{type checking} at compile time. This becomes clear by looking at the DSL program -\inputencoding{latin9}\lstinline!prgFile!\inputencoding{utf8} shown -in the previous section. The code of \inputencoding{latin9}\lstinline!runFile!\inputencoding{utf8} -assumes that the \inputencoding{latin9}\lstinline!Read!\inputencoding{utf8} -operation is always applied to a \inputencoding{latin9}\lstinline!Path!\inputencoding{utf8}. -However, this assumption is not enforced by the DSL. When composing -a larger DSL program from separately defined parts, one could by mistake -create an invalid DSL program such as \inputencoding{latin9}\lstinline!Read(Read(Val("file")))!\inputencoding{utf8} -where the \inputencoding{latin9}\lstinline!Read!\inputencoding{utf8} -operation is used incorrectly. Running this program causes a run-time -error:\inputencoding{latin9} +\lstinline!prgFile! shown in the previous section. The code of \lstinline!runFile! +assumes that the \lstinline!Read! operation is always applied to +a \lstinline!Path!. However, this assumption is not enforced by the +DSL. When composing a larger DSL program from separately defined parts, +one could by mistake create an invalid DSL program such as \lstinline!Read(Read(Val("file")))! +where the \lstinline!Read! operation is used incorrectly. Running +this program causes a run-time error: \begin{lstlisting} scala> runFile(Read(Read(Val("file")))) java.lang.Exception: Illegal PrgFile operation: Read(Read(Val(file))) \end{lstlisting} -\inputencoding{utf8}All DSL program values are of the same type (\inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8}) +All DSL program values are of the same type (\lstinline!PrgFile!) regardless of their intended meaning. So, the Scala compiler cannot verify that we are using the DSL correctly. It would be better if -the DSL program \inputencoding{latin9}\lstinline!Read(Read(Val("file")))!\inputencoding{utf8} -failed to compile. To achieve that, we need to use different Scala -types for DSL programs returning a \inputencoding{latin9}\lstinline!String!\inputencoding{utf8} -and a \inputencoding{latin9}\lstinline!Path!\inputencoding{utf8}. -So, let us replace the type \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} -by a type constructor \inputencoding{latin9}\lstinline!PrgFile[A]!\inputencoding{utf8}, -representing a DSL program that will return a value of type \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -when we run it:\inputencoding{latin9} +the DSL program \lstinline!Read(Read(Val("file")))! failed to compile. +To achieve that, we need to use different Scala types for DSL programs +returning a \lstinline!String! and a \lstinline!Path!. So, let us +replace the type \lstinline!PrgFile! by a type constructor \lstinline!PrgFile[A]!, +representing a DSL program that will return a value of type \lstinline!A! +when we run it: \begin{lstlisting} import java.nio.file.{Path => JPath} import java.nio.file.{Files, Paths} @@ -197,8 +190,8 @@ \subsection{Stage 2: implementing type safety in the DSLs} case Read(p) => new String(Files.readAllBytes(runFile(p))) } \end{lstlisting} -\inputencoding{utf8}Our example DSL is now fully type-safe.\index{type safety} Invalid -programs are rejected at compile time:\inputencoding{latin9} +Our example DSL is now fully type-safe.\index{type safety} Invalid +programs are rejected at compile time: \begin{lstlisting} val prgFile: PrgFile[String] = Read(Path(Read(Path(Val("config_location.txt"))))) @@ -213,32 +206,28 @@ \subsection{Stage 2: implementing type safety in the DSLs} ^ Compilation Failed \end{lstlisting} -\inputencoding{utf8} + A similar problem exists in our DSL for complex numbers. The code -of \inputencoding{latin9}\lstinline!runComplex!\inputencoding{utf8} -assumes that the \inputencoding{latin9}\lstinline!Rotate!\inputencoding{utf8} -operation is always applied to a \inputencoding{latin9}\lstinline!Phase!\inputencoding{utf8}, -but the DSL does not enforce that assumption. Both arguments of the -\inputencoding{latin9}\lstinline!Rotate!\inputencoding{utf8} operation -are values of type \inputencoding{latin9}\lstinline!PrgComplex!\inputencoding{utf8}, -and so the Scala compiler is unable to verify that the DSL is being -used correctly. If the programmer uses a plain complex number (\inputencoding{latin9}\lstinline!Val!\inputencoding{utf8}) -instead of a \inputencoding{latin9}\lstinline!Phase!\inputencoding{utf8} +of \lstinline!runComplex! assumes that the \lstinline!Rotate! operation +is always applied to a \lstinline!Phase!, but the DSL does not enforce +that assumption. Both arguments of the \lstinline!Rotate! operation +are values of type \lstinline!PrgComplex!, and so the Scala compiler +is unable to verify that the DSL is being used correctly. If the programmer +uses a plain complex number (\lstinline!Val!) instead of a \lstinline!Phase! value, the program will return an incorrect result \emph{without} -any indication of error:\inputencoding{latin9} +any indication of error: \begin{lstlisting} val prgComplex3: PrgComplex = Rotate(prgComplex1, Val(Complex(0, 1))) // Forgot Phase()! scala> runComplex(prgComplex3) // The result is incorrect, but there is no error message! val res5: Complex = Complex(11.0, -2.0) \end{lstlisting} -\inputencoding{utf8} -To achieve type safety, we add a type parameter to \inputencoding{latin9}\lstinline!PrgComplex!\inputencoding{utf8}. -A DSL program of type \inputencoding{latin9}\lstinline!PrgComplex[A]!\inputencoding{utf8} -will return a value of type \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -when run. (So, the new DSL programs will be able to compute values -of any type, not only of type \inputencoding{latin9}\lstinline!Complex!\inputencoding{utf8}.) -The new definitions of the case classes are:\inputencoding{latin9} + +To achieve type safety, we add a type parameter to \lstinline!PrgComplex!. +A DSL program of type \lstinline!PrgComplex[A]! will return a value +of type \lstinline!A! when run. (So, the new DSL programs will be +able to compute values of any type, not only of type \lstinline!Complex!.) +The new definitions of the case classes are: \begin{lstlisting} sealed trait PrgComplex[A] final case class Val(c: Complex) extends PrgComplex[Complex] @@ -248,9 +237,8 @@ \subsection{Stage 2: implementing type safety in the DSLs} final case class Phase(p: PrgComplex[Complex]) extends PrgComplex[Double] final case class Rotate(p: PrgComplex[Complex], a: Phase) extends PrgComplex[Complex] \end{lstlisting} -\inputencoding{utf8}The revised code of \inputencoding{latin9}\lstinline!runComplex!\inputencoding{utf8} -is clearer because phases are no longer wrapped in a \inputencoding{latin9}\lstinline!Complex!\inputencoding{utf8} -type:\inputencoding{latin9} +The revised code of \lstinline!runComplex! is clearer because phases +are no longer wrapped in a \lstinline!Complex! type: \begin{lstlisting} def runComplex[A]: PrgComplex[A] => A = { case Val(c) => c @@ -261,9 +249,8 @@ \subsection{Stage 2: implementing type safety in the DSLs} case Rotate(p, alpha) => runComplex(p).rotate(runComplex(alpha)) } \end{lstlisting} -\inputencoding{utf8}The example programs, \inputencoding{latin9}\lstinline!prgComplex1!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!prgComplex2!\inputencoding{utf8}, -do not change except for their type:\inputencoding{latin9} +The example programs, \lstinline!prgComplex1! and \lstinline!prgComplex2!, +do not change except for their type: \begin{lstlisting} val prgComplex1: PrgComplex[Complex] = Conj(Mul(Val(Complex(1, 2)), Val(Complex(3, -4)))) @@ -275,10 +262,8 @@ \subsection{Stage 2: implementing type safety in the DSLs} scala> runComplex(prgComplex2) res7: Complex = Complex(x = 2.000000000000001, y = 11.0) \end{lstlisting} -\inputencoding{utf8}Since \inputencoding{latin9}\lstinline!Rotate!\inputencoding{utf8} -now requires a \inputencoding{latin9}\lstinline!PrgComplex[Double]!\inputencoding{utf8}, -forgetting to use \inputencoding{latin9}\lstinline!Phase!\inputencoding{utf8} -will cause a type error:\inputencoding{latin9} +Since \lstinline!Rotate! now requires a \lstinline!PrgComplex[Double]!, +forgetting to use \lstinline!Phase! will cause a type error: \begin{lstlisting} scala> val prgComplex3 = Rotate(prgComplex1, Val(Complex(0, 1))) :1: type mismatch; @@ -288,7 +273,7 @@ \subsection{Stage 2: implementing type safety in the DSLs} ^ Compilation Failed \end{lstlisting} -\inputencoding{utf8} + \subsection{Stage 3: implementing bound variables} @@ -304,81 +289,72 @@ \subsection{Stage 3: implementing bound variables} As an example, consider the task of reading a file only if it exists and reporting an error otherwise. A direct (non-DSL) code for this -computation looks like this:\inputencoding{latin9} +computation looks like this: \begin{lstlisting} val p = Paths.get("config_location.txt") val result = if (Files.exists(p)) new String(Files.readAllBytes(p)) else "No file." \end{lstlisting} -\inputencoding{utf8}Trying to translate the above code to the \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} -DSL, we find that we need to set the Scala variable \inputencoding{latin9}\lstinline!p!\inputencoding{utf8} -to the path computed by a previous operation. Here is a first attempt:\inputencoding{latin9} +Trying to translate the above code to the \lstinline!PrgFile! DSL, +we find that we need to set the Scala variable \lstinline!p! to the +path computed by a previous operation. Here is a first attempt: \begin{lstlisting} val p: JPath = runFile(Path(Val("config_location.txt"))) val prg: PrgFile[String] = if (Files.exists(p)) Read(p) else Val("No file.") \end{lstlisting} -\inputencoding{utf8}There are two problems with this code. The first problem is that \inputencoding{latin9}\lstinline!Read(p)!\inputencoding{utf8} -does not compile because the argument of \inputencoding{latin9}\lstinline!Read!\inputencoding{utf8} -should be a \inputencoding{latin9}\lstinline!PrgFile[JPath]!\inputencoding{utf8} -rather than just a \inputencoding{latin9}\lstinline!JPath!\inputencoding{utf8}. -To solve this problem, we generalize the \inputencoding{latin9}\lstinline!Val!\inputencoding{utf8} -class to hold values of arbitrary type:\inputencoding{latin9} +There are two problems with this code. The first problem is that \lstinline!Read(p)! +does not compile because the argument of \lstinline!Read! should +be a \lstinline!PrgFile[JPath]! rather than just a \lstinline!JPath!. +To solve this problem, we generalize the \lstinline!Val! class to +hold values of arbitrary type: \begin{lstlisting} final case class Val[A](a: A) extends PrgFile[A] \end{lstlisting} -\inputencoding{utf8}We then replace \inputencoding{latin9}\lstinline!Read(p)!\inputencoding{utf8} -by \inputencoding{latin9}\lstinline!Read(Val(p))!\inputencoding{utf8} -in the code above. The code of \inputencoding{latin9}\lstinline!run!\inputencoding{utf8} -remains unchanged. +We then replace \lstinline!Read(p)! by \lstinline!Read(Val(p))! +in the code above. The code of \lstinline!run! remains unchanged. -The second problem is that we are using \inputencoding{latin9}\lstinline!runFile!\inputencoding{utf8} -in the middle of a DSL program. This breaks the assumption that a -DSL program merely describes the computations without executing them. -The use of a specific runner inside a DSL program will also prevent -us from being able to apply a different runner to the entire DSL program -later. +The second problem is that we are using \lstinline!runFile! in the +middle of a DSL program. This breaks the assumption that a DSL program +merely describes the computations without executing them. The use +of a specific runner inside a DSL program will also prevent us from +being able to apply a different runner to the entire DSL program later. To solve this problem, we introduce a new DSL operation that binds Scala variables to values returned by other DSL operations. A standard way of creating \index{bound variable}bound variables is by using those variables as arguments of nameless functions. So, let us consider -the nameless function:\inputencoding{latin9} +the nameless function: \begin{lstlisting} { p => if (Files.exists(p)) Read(Val(p)) else Val("No file.") } \end{lstlisting} -\inputencoding{utf8}This function creates a local variable \inputencoding{latin9}\lstinline!p!\inputencoding{utf8} -and uses it directly in an expression of type \inputencoding{latin9}\lstinline!PrgFile[String]!\inputencoding{utf8}. -We can call this function at the time when the entire DSL program -is run. So, we replace the code \inputencoding{latin9}\lstinline!val p = runFile(...)!\inputencoding{utf8} -by a new DSL operation that we will call \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8}. -The above code that computes \inputencoding{latin9}\lstinline!p!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!prg!\inputencoding{utf8} is -replaced by:\inputencoding{latin9} +This function creates a local variable \lstinline!p! and uses it +directly in an expression of type \lstinline!PrgFile[String]!. We +can call this function at the time when the entire DSL program is +run. So, we replace the code \lstinline!val p = runFile(...)! by +a new DSL operation that we will call \lstinline!Bind!. The above +code that computes \lstinline!p! and \lstinline!prg! is replaced +by: \begin{lstlisting} val prg2: PrgFile[JPath] = Path(Val("config.txt")) val prg: PrgFile[String] = Bind(prg2){ p => if (Files.exists(p)) Read(Val(p)) else Val("No file.") } \end{lstlisting} -\inputencoding{utf8}Here, the case class \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} -has two curried arguments: a value of type \inputencoding{latin9}\lstinline!PrgFile[JPath]!\inputencoding{utf8} -and a function of type \inputencoding{latin9}\lstinline!JPath => PrgFile[String]!\inputencoding{utf8}. -That function will be called by \inputencoding{latin9}\lstinline!runFile!\inputencoding{utf8} -when we run the DSL program. At that time, the argument \inputencoding{latin9}\lstinline!p!\inputencoding{utf8} -will be set to the value obtained by running \inputencoding{latin9}\lstinline!prg2!\inputencoding{utf8}. -We will implement this logic in the new code for \inputencoding{latin9}\lstinline!runFile!\inputencoding{utf8} -(shown below). +Here, the case class \lstinline!Bind! has two curried arguments: +a value of type \lstinline!PrgFile[JPath]! and a function of type +\lstinline!JPath => PrgFile[String]!. That function will be called +by \lstinline!runFile! when we run the DSL program. At that time, +the argument \lstinline!p! will be set to the value obtained by running +\lstinline!prg2!. We will implement this logic in the new code for +\lstinline!runFile! (shown below). In a different DSL program, we may need to use different types instead -of \inputencoding{latin9}\lstinline!String!\inputencoding{utf8} and -\inputencoding{latin9}\lstinline!JPath!\inputencoding{utf8}. So, -let us replace those types by type parameters \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!B!\inputencoding{utf8}. The -declaration of the class \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} -becomes:\inputencoding{latin9} +of \lstinline!String! and \lstinline!JPath!. So, let us replace +those types by type parameters \lstinline!A! and \lstinline!B!. +The declaration of the class \lstinline!Bind! becomes: \begin{lstlisting} final case class Bind[A, B](pa: PrgFile[B])(f: B => PrgFile[A]) extends PrgFile[A] \end{lstlisting} -\inputencoding{utf8} -After adding the \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8}-handling -code to the runner, the implementation of the DSL is:\inputencoding{latin9} + +After adding the \lstinline!Bind!-handling code to the runner, the +implementation of the DSL is: \begin{lstlisting} sealed trait PrgFile[A] final case class Val[A](a: A) extends PrgFile[A] @@ -393,59 +369,50 @@ \subsection{Stage 3: implementing bound variables} case Read(p) => new String(Files.readAllBytes(runFile(p))) } \end{lstlisting} -\inputencoding{utf8} + Let us explain certain features of Scala used in this DSL. Since the -arguments of the constructor \inputencoding{latin9}\lstinline!Bind[A, B]!\inputencoding{utf8} -are curried, the first curried argument (of type \inputencoding{latin9}\lstinline!PrgFile[B]!\inputencoding{utf8}) -will allow the Scala compiler to determine the type parameter \inputencoding{latin9}\lstinline!B!\inputencoding{utf8} -automatically. At the same time, \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8}\textsf{'}s -second curried argument may be written as a nameless function in a -separate set of braces:\inputencoding{latin9} +arguments of the constructor \lstinline!Bind[A, B]! are curried, +the first curried argument (of type \lstinline!PrgFile[B]!) will +allow the Scala compiler to determine the type parameter \lstinline!B! +automatically. At the same time, \lstinline!Bind!\textsf{'}s second curried +argument may be written as a nameless function in a separate set of +braces: \begin{lstlisting} val prg: PrgFile[String] = Bind(prg2){ p => if (Files.exists(p)) Read(Val(p)) else Val("No file.") } \end{lstlisting} -\inputencoding{utf8}Because the type parameter \inputencoding{latin9}\lstinline!B!\inputencoding{utf8} -is already fixed, the argument \inputencoding{latin9}\lstinline!p!\inputencoding{utf8} -may be written without a type annotation (in this case, it would have -been \inputencoding{latin9}\lstinline!p:JPath!\inputencoding{utf8}). -This makes the DSL easier to use. However, the underlying implementation -becomes more complicated. Scala\textsf{'}s pattern matching syntax is limited -to the first curried argument. So, the second curried argument of -\inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} needs -a \inputencoding{latin9}\lstinline!val!\inputencoding{utf8} declaration -and is accessed using another pattern variable (here called \inputencoding{latin9}\lstinline!bind!\inputencoding{utf8}). -Finally, the type parameters \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!B!\inputencoding{utf8} are defined -such that \inputencoding{latin9}\lstinline!Bind[A, B]!\inputencoding{utf8} -is a subtype of \inputencoding{latin9}\lstinline!PrgFile[A]!\inputencoding{utf8} -and not of \inputencoding{latin9}\lstinline!PrgFile[B]!\inputencoding{utf8}. +Because the type parameter \lstinline!B! is already fixed, the argument +\lstinline!p! may be written without a type annotation (in this case, +it would have been \lstinline!p:JPath!). This makes the DSL easier +to use. However, the underlying implementation becomes more complicated. +Scala\textsf{'}s pattern matching syntax is limited to the first curried argument. +So, the second curried argument of \lstinline!Bind! needs a \lstinline!val! +declaration and is accessed using another pattern variable (here called +\lstinline!bind!). Finally, the type parameters \lstinline!A! and +\lstinline!B! are defined such that \lstinline!Bind[A, B]! is a +subtype of \lstinline!PrgFile[A]! and not of \lstinline!PrgFile[B]!. This allows Scala\textsf{'}s type checking to handle correctly the unknown -type \inputencoding{latin9}\lstinline!B!\inputencoding{utf8} in the -pattern \inputencoding{latin9}\lstinline!Bind(pa)!\inputencoding{utf8}. +type \lstinline!B! in the pattern \lstinline!Bind(pa)!. -The new \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} -operation allows one part of a DSL program to use values computed -by other parts. Since a \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} -value contains a \emph{Scala function} (of type \inputencoding{latin9}\lstinline!B => PrgFile[A]!\inputencoding{utf8}), +The new \lstinline!Bind! operation allows one part of a DSL program +to use values computed by other parts. Since a \lstinline!Bind! value +contains a \emph{Scala function} (of type \lstinline!B => PrgFile[A]!), that function may run arbitrary Scala code, not limited to DSL operations, -in order to compute a result of type \inputencoding{latin9}\lstinline!PrgFile[A]!\inputencoding{utf8}. -All non-DSL code is enclosed inside a \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} -and will be evaluated only when the DSL program is run:\inputencoding{latin9} +in order to compute a result of type \lstinline!PrgFile[A]!. All +non-DSL code is enclosed inside a \lstinline!Bind! and will be evaluated +only when the DSL program is run: \begin{lstlisting} scala> runFile(prg) res8: String = "version = 1" \end{lstlisting} -\inputencoding{utf8} As before, a value of type \inputencoding{latin9}\lstinline!PrgFile[A]!\inputencoding{utf8} -describes a computation but does not execute it. + As before, a value of type \lstinline!PrgFile[A]! describes a computation +but does not execute it. \subsection{Stage 4: refactoring to a monadic DSL} -The next observation is that \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Val!\inputencoding{utf8} have -the same type signatures as the standard \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!pure!\inputencoding{utf8} methods -of a monad. For convenience, let us define those methods on the \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} -class:\inputencoding{latin9} +The next observation is that \lstinline!Bind! and \lstinline!Val! +have the same type signatures as the standard \lstinline!flatMap! +and \lstinline!pure! methods of a monad. For convenience, let us +define those methods on the \lstinline!PrgFile! class: \begin{lstlisting} sealed trait PrgFile[A] { def flatMap[B](f: A => PrgFile[B]): PrgFile[B] = Bind(this)(f) @@ -460,29 +427,25 @@ \subsection{Stage 4: refactoring to a monadic DSL} def pure[A](a: A): PrgFile[A] = Val(a) } \end{lstlisting} -\inputencoding{utf8}Here we defined \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -via \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -by using a monad\textsf{'}s right identity law, as shown in Eq.~(\ref{eq:express-map-through-flatMap}). +Here we defined \lstinline!map! via \lstinline!flatMap! by using +a monad\textsf{'}s right identity law, as shown in Eq.~(\ref{eq:express-map-through-flatMap}). -Like other DSL operations, the methods \inputencoding{latin9}\lstinline!map!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8}, and -\inputencoding{latin9}\lstinline!pure!\inputencoding{utf8} do not -run or evaluate any parts of a DSL program. They only create some -nested data structures containing not-yet-called functions. The necessary -logic will be executed when the \inputencoding{latin9}\lstinline!run!\inputencoding{utf8} +Like other DSL operations, the methods \lstinline!map!, \lstinline!flatMap!, +and \lstinline!pure! do not run or evaluate any parts of a DSL program. +They only create some nested data structures containing not-yet-called +functions. The necessary logic will be executed when the \lstinline!run! method is invoked. At that time, the entire DSL program will be converted into a result value. -Because \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -are defined, DSL programs can now be written as functor blocks:\inputencoding{latin9} +Because \lstinline!map! and \lstinline!flatMap! are defined, DSL +programs can now be written as functor blocks: \begin{lstlisting} val prg: PrgFile[String] = for { p <- Path(Val("config.txt")) r <- if (Files.exists(p)) Read(Val(p)) else Val("No file.") } yield r \end{lstlisting} -\inputencoding{utf8}Longer DSL programs may be composed from shorter ones:\inputencoding{latin9} +Longer DSL programs may be composed from shorter ones: \begin{lstlisting} def readFileContents(filename: String): PrgFile[String] = for { path <- Path(Val(filename)) @@ -497,23 +460,18 @@ \subsection{Stage 4: refactoring to a monadic DSL} scala> runFile(prg2) res9 : String = "version = 1" \end{lstlisting} -\inputencoding{utf8} -Another benefit of the monadic refactoring is that \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} + +Another benefit of the monadic refactoring is that \lstinline!flatMap! provides a general way of applying a file operation to a value wrapped -under \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8}. -So, we may simplify the case classes \inputencoding{latin9}\lstinline!Path!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Read!\inputencoding{utf8} by -removing the \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} -wrapping:\inputencoding{latin9} +under \lstinline!PrgFile!. So, we may simplify the case classes \lstinline!Path! +and \lstinline!Read! by removing the \lstinline!PrgFile! wrapping: \begin{lstlisting} final case class Path(p: String) extends PrgFile[JPath] final case class Read(p: JPath) extends PrgFile[String] \end{lstlisting} -\inputencoding{utf8}This simplifies the usage of the DSL since we may write, e.g., just -\inputencoding{latin9}\lstinline!Read(path)!\inputencoding{utf8} -instead of \inputencoding{latin9}\lstinline!Read(Val(path))!\inputencoding{utf8}. -The code of \inputencoding{latin9}\lstinline!runFile!\inputencoding{utf8} -is also shortened:\inputencoding{latin9} +This simplifies the usage of the DSL since we may write, e.g., just +\lstinline!Read(path)! instead of \lstinline!Read(Val(path))!. The +code of \lstinline!runFile! is also shortened: \begin{lstlisting} def runFile[A]: PrgFile[A] => A = { case Val(a) => a @@ -522,12 +480,12 @@ \subsection{Stage 4: refactoring to a monadic DSL} case Read(p) => new String(Files.readAllBytes(p)) } \end{lstlisting} -\inputencoding{utf8} + \subsection{Stage 5: refactoring to reuse common code\label{subsec:Stage-5:-refactoring-monadDSL}} Let us rewrite our second example (a DSL for complex numbers) in the -same way:\inputencoding{latin9} +same way: \begin{lstlisting} sealed trait PrgComplex[A] { def flatMap[B](f: A => PrgComplex[B]): PrgComplex[B] = Bind(this)(f) @@ -555,11 +513,9 @@ \subsection{Stage 5: refactoring to reuse common code\label{subsec:Stage-5:-refa case Rotate(p, Phase(a)) => p.rotate(a.phase) } \end{lstlisting} -\inputencoding{utf8} -Comparing the code of \inputencoding{latin9}\lstinline!runFile!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!runComplex!\inputencoding{utf8}, -we notice that the \inputencoding{latin9}\lstinline!Val!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} operations + +Comparing the code of \lstinline!runFile! and \lstinline!runComplex!, +we notice that the \lstinline!Val! and \lstinline!Bind! operations are implemented in the same way. The differences are in the code for operations specific to file handling or to complex numbers. So, let us refactor these data structures, separating the domain-specific @@ -569,14 +525,12 @@ \subsection{Stage 5: refactoring to reuse common code\label{subsec:Stage-5:-refa as a type parameter. Let us now refactor the two example DSLs in this manner. -The effect constructor for the \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} -DSL should contain the operations \inputencoding{latin9}\lstinline!Path!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Read!\inputencoding{utf8}. Let -us copy the existing case classes for these operations but rename -their parent type to \inputencoding{latin9}\lstinline!PrgFileC!\inputencoding{utf8}. -We will also move the relevant parts of \inputencoding{latin9}\lstinline!runFile!\inputencoding{utf8} -into a new function (\inputencoding{latin9}\lstinline!runFileC!\inputencoding{utf8}) -that we call an \textbf{effect runner}\index{effect runner}:\inputencoding{latin9} +The effect constructor for the \lstinline!PrgFile! DSL should contain +the operations \lstinline!Path! and \lstinline!Read!. Let us copy +the existing case classes for these operations but rename their parent +type to \lstinline!PrgFileC!. We will also move the relevant parts +of \lstinline!runFile! into a new function (\lstinline!runFileC!) +that we call an \textbf{effect runner}\index{effect runner}: \begin{lstlisting} sealed trait PrgFileC[A] final case class Path(p: String) extends PrgFileC[JPath] @@ -587,8 +541,8 @@ \subsection{Stage 5: refactoring to reuse common code\label{subsec:Stage-5:-refa case Read(p) => new String(Files.readAllBytes(p)) } \end{lstlisting} -\inputencoding{utf8}The code of \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} -becomes shorter since we moved some code out of it:\inputencoding{latin9} +The code of \lstinline!PrgFile! becomes shorter since we moved some +code out of it: \begin{lstlisting} // The same code for sealed trait PrgFile[A] and object PrgFile. // The case classes are changed to: @@ -602,9 +556,8 @@ \subsection{Stage 5: refactoring to reuse common code\label{subsec:Stage-5:-refa case Op(op) => runFileC(op) // Run the custom operation and get the result. } \end{lstlisting} -\inputencoding{utf8} -The code of \inputencoding{latin9}\lstinline!PrgComplex!\inputencoding{utf8} -is refactored in a similar way:\inputencoding{latin9} + +The code of \lstinline!PrgComplex! is refactored in a similar way: \begin{lstlisting} // The same code for `sealed trait PrgComplex[A]` and `object PrgComplex`. // The case classes are changed to: @@ -632,24 +585,19 @@ \subsection{Stage 5: refactoring to reuse common code\label{subsec:Stage-5:-refa case Rotate(p, Phase(a)) => p.rotate(a.phase) } \end{lstlisting} -\inputencoding{utf8} -The code of \inputencoding{latin9}\lstinline!PrgComplex!\inputencoding{utf8} -is now the same as the code of \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} -except for using a different effect constructor (\inputencoding{latin9}\lstinline!PrgComplexC!\inputencoding{utf8} -instead of \inputencoding{latin9}\lstinline!PrgFileC!\inputencoding{utf8}) -and the corresponding runner (\inputencoding{latin9}\lstinline!runComplexC!\inputencoding{utf8} -instead of \inputencoding{latin9}\lstinline!runFileC!\inputencoding{utf8}). + +The code of \lstinline!PrgComplex! is now the same as the code of +\lstinline!PrgFile! except for using a different effect constructor +(\lstinline!PrgComplexC! instead of \lstinline!PrgFileC!) and the +corresponding runner (\lstinline!runComplexC! instead of \lstinline!runFileC!). The effect constructor and its runner encapsulate the entire domain-specific -logic. The code in the classes \inputencoding{latin9}\lstinline!PrgComplex!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} +logic. The code in the classes \lstinline!PrgComplex! and \lstinline!PrgFile! is only concerned with providing the monadic functionality to the DSL. We can replace those two classes by a single class (called, say, -\inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}) that -takes the effect constructor as a \emph{type parameter} \inputencoding{latin9}\lstinline!F!\inputencoding{utf8}. -Since the type parameter \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} +\lstinline!MonadDSL!) that takes the effect constructor as a \emph{type +parameter} \lstinline!F!. Since the type parameter \lstinline!F! is itself a type constructor, we must declare it via the Scala syntax -\inputencoding{latin9}\lstinline!F[_]!\inputencoding{utf8}. The code -is:\inputencoding{latin9} +\lstinline!F[_]!. The code is: \begin{lstlisting} sealed trait MonadDSL[F[_], A] { def flatMap[B](f: A => MonadDSL[F, B]): MonadDSL[F, B] = Bind(this)(f) @@ -662,12 +610,11 @@ \subsection{Stage 5: refactoring to reuse common code\label{subsec:Stage-5:-refa final case class Bind[F[_], A, B](pa: MonadDSL[F, B])(val f: B => MonadDSL[F, A]) extends MonadDSL[F, A] final case class Op[F[_], A](op: F[A]) extends MonadDSL[F, A] // Wrap all domain-specific operations. \end{lstlisting} -\inputencoding{utf8} -The runner for \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -needs to run the effects described by the effect constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8}. -We try passing \inputencoding{latin9}\lstinline!F!\inputencoding{utf8}\textsf{'}s -runner as an additional curried argument to \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}\textsf{'}s -runner:\inputencoding{latin9} + +The runner for \lstinline!MonadDSL! needs to run the effects described +by the effect constructor \lstinline!F!. We try passing \lstinline!F!\textsf{'}s +runner as an additional curried argument to \lstinline!MonadDSL!\textsf{'}s +runner: \begin{lstlisting} def run[F[_], A](runner: F[A] => A): MonadDSL[F, A] => A = { // This code does not compile. case Val(a) => a @@ -675,69 +622,58 @@ \subsection{Stage 5: refactoring to reuse common code\label{subsec:Stage-5:-refa case Op(op) => runner(op) } \end{lstlisting} -\inputencoding{utf8}However, this code gives a type error. The type of \inputencoding{latin9}\lstinline!bind.f!\inputencoding{utf8} -is \inputencoding{latin9}\lstinline!B => MonadDSL[F, A]!\inputencoding{utf8}, -so the argument of \inputencoding{latin9}\lstinline!bind.f!\inputencoding{utf8} -must have type \inputencoding{latin9}\lstinline!B!\inputencoding{utf8}. -A value of type \inputencoding{latin9}\lstinline!B!\inputencoding{utf8} -will be produced by \inputencoding{latin9}\lstinline!run(runner)(pa)!\inputencoding{utf8} -only if we set type parameters as \inputencoding{latin9}\lstinline!run[F, B]!\inputencoding{utf8}, -and only if \inputencoding{latin9}\lstinline!runner!\inputencoding{utf8} -has type \inputencoding{latin9}\lstinline!F[B] => B!\inputencoding{utf8}. -But the given argument \inputencoding{latin9}\lstinline!runner!\inputencoding{utf8} -has a fixed type \inputencoding{latin9}\lstinline!F[A] => A!\inputencoding{utf8} -and cannot be used with type \inputencoding{latin9}\lstinline!B!\inputencoding{utf8}. - -The function \inputencoding{latin9}\lstinline!run(runner)!\inputencoding{utf8} -will work with arbitrary types only if \inputencoding{latin9}\lstinline!runner!\inputencoding{utf8} -has type \inputencoding{latin9}\lstinline!F[X] => X!\inputencoding{utf8} -where \inputencoding{latin9}\lstinline!X!\inputencoding{utf8} remains -free and is \emph{not} known in advance. The type signatures of the -effect runners \inputencoding{latin9}\lstinline!runComplexC!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!runFileC!\inputencoding{utf8} -already have that form:\inputencoding{latin9} +However, this code gives a type error. The type of \lstinline!bind.f! +is \lstinline!B => MonadDSL[F, A]!, so the argument of \lstinline!bind.f! +must have type \lstinline!B!. A value of type \lstinline!B! will +be produced by \lstinline!run(runner)(pa)! only if we set type parameters +as \lstinline!run[F, B]!, and only if \lstinline!runner! has type +\lstinline!F[B] => B!. But the given argument \lstinline!runner! +has a fixed type \lstinline!F[A] => A! and cannot be used with type +\lstinline!B!. + +The function \lstinline!run(runner)! will work with arbitrary types +only if \lstinline!runner! has type \lstinline!F[X] => X! where +\lstinline!X! remains free and is \emph{not} known in advance. The +type signatures of the effect runners \lstinline!runComplexC! and +\lstinline!runFileC! already have that form: \begin{lstlisting} def runComplexC[X]: PrgComplexC[X] => X // PrgComplexC[X] => X with arbitrary types X def runFileC[X]: PrgFileC[X] => X // PrgFileC[X] => X with arbitrary types X \end{lstlisting} -\inputencoding{utf8}We need to pass those functions as the \inputencoding{latin9}\lstinline!runner!\inputencoding{utf8} -arguments to \inputencoding{latin9}\lstinline!run(runner)!\inputencoding{utf8} -while not losing the freedom provided by the type parameter \inputencoding{latin9}\lstinline!X!\inputencoding{utf8}. +We need to pass those functions as the \lstinline!runner! arguments +to \lstinline!run(runner)! while not losing the freedom provided +by the type parameter \lstinline!X!. -To achieve that, we define a new type \inputencoding{latin9}\lstinline!Runner!\inputencoding{utf8} -as a Scala trait encapsulating the type signature of an effect runner. -The two effect runners can then be declared as values of type \inputencoding{latin9}\lstinline!Runner!\inputencoding{utf8}:\inputencoding{latin9} +To achieve that, we define a new type \lstinline!Runner! as a Scala +trait encapsulating the type signature of an effect runner. The two +effect runners can then be declared as values of type \lstinline!Runner!: \begin{lstlisting} trait Runner[F[_]] { def run[X]: F[X] => X } val runnerComplex = new Runner[PrgComplexC] { def run[X]: PrgComplexC[X] => X = runComplexC[X] } val runnerFile = new Runner[PrgFileC] { def run[X]: PrgFileC[X] => X = runFileC[X] } \end{lstlisting} -\inputencoding{utf8} + Function values that encapsulate a type parameter are called \textbf{polymorphic functions}\index{polymorphic function}. The type notation for the -type \inputencoding{latin9}\lstinline!Runner!\inputencoding{utf8} -is: +type \lstinline!Runner! is: \[ \text{Runner}^{F}\triangleq\forall X.\,F^{X}\rightarrow X\quad. \] The universal quantifier ($\forall X$) indicates that a value of -type \inputencoding{latin9}\lstinline!Runner[F]!\inputencoding{utf8} -still has the freedom of using any type \inputencoding{latin9}\lstinline!X!\inputencoding{utf8} -when the method \inputencoding{latin9}\lstinline!run!\inputencoding{utf8} -is called. +type \lstinline!Runner[F]! still has the freedom of using any type +\lstinline!X! when the method \lstinline!run! is called. -In Scala 3, the type of an effect runner is written in a shorter syntax:\inputencoding{latin9} +In Scala 3, the type of an effect runner is written in a shorter syntax: \begin{lstlisting} type Runner[F[_]] = [X] => F[X] => X \end{lstlisting} -\inputencoding{utf8}This corresponds to the type notation $\forall X.\,F^{X}\rightarrow X$. -The programmer does not need to declare a trait \inputencoding{latin9}\lstinline!Runner!\inputencoding{utf8} +This corresponds to the type notation $\forall X.\,F^{X}\rightarrow X$. +The programmer does not need to declare a trait \lstinline!Runner! in Scala 3 because the compiler does that automatically for polymorphic functions. -Using the \inputencoding{latin9}\lstinline!Runner!\inputencoding{utf8} -type, we rewrite the code for the function \inputencoding{latin9}\lstinline!run!\inputencoding{utf8} -as:\inputencoding{latin9} +Using the \lstinline!Runner! type, we rewrite the code for the function +\lstinline!run! as: \begin{lstlisting} def run[F[_], A](runner: Runner[F]): MonadDSL[F, A] => A = { case Val(a) => a @@ -745,9 +681,8 @@ \subsection{Stage 5: refactoring to reuse common code\label{subsec:Stage-5:-refa case Op(op) => runner.run(op) } \end{lstlisting} -\inputencoding{utf8}We can now define \inputencoding{latin9}\lstinline!PrgComplex!\inputencoding{utf8} -through \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -and test the new code:\inputencoding{latin9} +We can now define \lstinline!PrgComplex! through \lstinline!MonadDSL! +and test the new code: \begin{lstlisting} type PrgComplex[A] = MonadDSL[PrgComplexC, A] val prgComplex: PrgComplex[Complex] = for { @@ -760,26 +695,23 @@ \subsection{Stage 5: refactoring to reuse common code\label{subsec:Stage-5:-refa scala> runComplex(runnerComplex)(prgComplex2) res0: Complex = Complex(x = 2.000000000000001, y = 11.0) \end{lstlisting} -\inputencoding{utf8} + \subsection{A first recipe for monadic DSLs\label{subsec:A-first-recipe-monadic-dsl}} The previous section gave motivation for defining the type constructor -\inputencoding{latin9}\lstinline!MonadDSL[F[_], A]!\inputencoding{utf8}. -That type constructor is called the \textsf{``}free monad on \inputencoding{latin9}\lstinline!F!\inputencoding{utf8}\textsf{''}, -for reasons explained later in this chapter. Note that \inputencoding{latin9}\lstinline!MonadDSL[F[_], A]!\inputencoding{utf8} -supports the monad\textsf{'}s methods \inputencoding{latin9}\lstinline!map!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8}, and -\inputencoding{latin9}\lstinline!pure!\inputencoding{utf8}, which -\inputencoding{latin9}\lstinline!F!\inputencoding{utf8} does not -necessarily have. So, we may view \inputencoding{latin9}\lstinline!MonadDSL[F[_], A]!\inputencoding{utf8} +\lstinline!MonadDSL[F[_], A]!. That type constructor is called the +\textsf{``}free monad on \lstinline!F!\textsf{''}, for reasons explained later in +this chapter. Note that \lstinline!MonadDSL[F[_], A]! supports the +monad\textsf{'}s methods \lstinline!map!, \lstinline!flatMap!, and \lstinline!pure!, +which \lstinline!F! does not necessarily have. So, we may view \lstinline!MonadDSL[F[_], A]! as a special wrapper that adds the monadic variable-binding functionality to any given DSL whose operations are described by an effect constructor -\inputencoding{latin9}\lstinline!F!\inputencoding{utf8}. +\lstinline!F!. Below we will show that monadic DSL programs will satisfy the monad laws \emph{after} the effects are run (but not necessarily before -that!). Let us now summarize a recipe for using \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} +that!). Let us now summarize a recipe for using \lstinline!MonadDSL! in practice. Begin by writing down the types of the available operations in the @@ -792,82 +724,71 @@ \subsection{A first recipe for monadic DSLs\label{subsec:A-first-recipe-monadic- $P$ and $Q$ are type expressions possibly depending on some type parameters. -The next step is to define a new type constructor \inputencoding{latin9}\lstinline!F[_]!\inputencoding{utf8} -that will serve as the DSL\textsf{'}s effect constructor\index{effect constructor}:\inputencoding{latin9} +The next step is to define a new type constructor \lstinline!F[_]! +that will serve as the DSL\textsf{'}s effect constructor\index{effect constructor}: \begin{lstlisting} sealed trait F[_] \end{lstlisting} -\inputencoding{utf8} The effect constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -will be a disjunctive type whose parts correspond to each of the domain-specific -operations. For an operation with type $P\rightarrow Q$, define the -corresponding case class as:\inputencoding{latin9} + The effect constructor \lstinline!F! will be a disjunctive type +whose parts correspond to each of the domain-specific operations. +For an operation with type $P\rightarrow Q$, define the corresponding +case class as: \begin{lstlisting} final case class ReadPWriteQ(x: P) extends F[Q] \end{lstlisting} -\inputencoding{utf8}The names of case classes (such as \inputencoding{latin9}\lstinline!ReadPWriteQ!\inputencoding{utf8}) -may be chosen for clarity. +The names of case classes (such as \lstinline!ReadPWriteQ!) may be +chosen for clarity. -It is an important part of the recipe that the result type (\inputencoding{latin9}\lstinline!Q!\inputencoding{utf8}) -is wrapped in the effect constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -while the argument type (\inputencoding{latin9}\lstinline!P!\inputencoding{utf8}) -is \emph{not}. For example, the \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} +It is an important part of the recipe that the result type (\lstinline!Q!) +is wrapped in the effect constructor \lstinline!F! while the argument +type (\lstinline!P!) is \emph{not}. For example, the \lstinline!PrgFile! DSL described in the previous section has two operations with types -\inputencoding{latin9}\lstinline!String => JPath!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!JPath => String!\inputencoding{utf8}. -The corresponding effect constructor (\inputencoding{latin9}\lstinline!PrgFileC!\inputencoding{utf8}) -is defined by:\inputencoding{latin9} +\lstinline!String => JPath! and \lstinline!JPath => String!. The +corresponding effect constructor (\lstinline!PrgFileC!) is defined +by: \begin{lstlisting} sealed trait PrgFileC[_] final case class Path(p: String) extends PrgFileC[JPath] final case class Read(p: JPath) extends PrgFileC[String] \end{lstlisting} -\inputencoding{utf8} -Typically, a type constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -defined in this way will be an \textsf{``}unfunctor\textsf{''}\index{unfunctor}. -In the example shown above, \inputencoding{latin9}\lstinline!PrgFileC!\inputencoding{utf8} -cannot be a functor because it is impossible to create values of type -\inputencoding{latin9}\lstinline!PrgFileC[A]!\inputencoding{utf8} -with an arbitrary type \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -(the type \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -must be either \inputencoding{latin9}\lstinline!JPath!\inputencoding{utf8} -or \inputencoding{latin9}\lstinline!String!\inputencoding{utf8}). -However, in some cases the effect constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -could be itself a lawful functor. + +Typically, a type constructor \lstinline!F! defined in this way will +be an \textsf{``}unfunctor\textsf{''}\index{unfunctor}. In the example shown above, +\lstinline!PrgFileC! cannot be a functor because it is impossible +to create values of type \lstinline!PrgFileC[A]! with an arbitrary +type \lstinline!A! (the type \lstinline!A! must be either \lstinline!JPath! +or \lstinline!String!). However, in some cases the effect constructor +\lstinline!F! could be itself a lawful functor. Next, we implement an \textsf{``}effect runner\textsf{''} \index{effect runner}for -\inputencoding{latin9}\lstinline!F!\inputencoding{utf8}. An \textbf{effect -runner} for an effect constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -is a polymorphic function\index{polymorphic function} with the type -signature $\forall X.\,F^{X}\rightarrow X$:\inputencoding{latin9} +\lstinline!F!. An \textbf{effect runner} for an effect constructor +\lstinline!F! is a polymorphic function\index{polymorphic function} +with the type signature $\forall X.\,F^{X}\rightarrow X$: \begin{lstlisting} val runF: Runner[F] = new Runner[F] { def run[X]: F[X] => X = ??? } \end{lstlisting} -\inputencoding{utf8}If the definition of \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -allows only certain types \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -in values of type \inputencoding{latin9}\lstinline!F[A]!\inputencoding{utf8} -then the runner only needs to work for those types. In the \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} -DSL, the runner only needs to work with arguments of types \inputencoding{latin9}\lstinline!PrgFileC[JPath]!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!PrgFileC[String]!\inputencoding{utf8} -because those are the only allowed types of values that can be wrapped -by the unfunctor \inputencoding{latin9}\lstinline!PrgFileC!\inputencoding{utf8}. -The runner will never be given an argument of type \inputencoding{latin9}\lstinline!PrgFile[X]!\inputencoding{utf8} -with an arbitrary unknown \inputencoding{latin9}\lstinline!X!\inputencoding{utf8}. -However, different effect constructors may have different allowed -types, and \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} +If the definition of \lstinline!F! allows only certain types \lstinline!A! +in values of type \lstinline!F[A]! then the runner only needs to +work for those types. In the \lstinline!PrgFile! DSL, the runner +only needs to work with arguments of types \lstinline!PrgFileC[JPath]! +and \lstinline!PrgFileC[String]! because those are the only allowed +types of values that can be wrapped by the unfunctor \lstinline!PrgFileC!. +The runner will never be given an argument of type \lstinline!PrgFile[X]! +with an arbitrary unknown \lstinline!X!. However, different effect +constructors may have different allowed types, and \lstinline!MonadDSL! needs to be able to work with any effect constructor. So, the runner\textsf{'}s -type signature still needs a general type parameter \inputencoding{latin9}\lstinline!X!\inputencoding{utf8}:\inputencoding{latin9} +type signature still needs a general type parameter \lstinline!X!: \begin{lstlisting} val runnerFile = new Runner[PrgFileC] { def run[X]: (PrgFileC[X] => X) = runFileC[X] } \end{lstlisting} -\inputencoding{utf8} -We now define the monadic DSL type (\inputencoding{latin9}\lstinline!MyDSL!\inputencoding{utf8}) -and its runner function (\inputencoding{latin9}\lstinline!runMyDSL!\inputencoding{utf8}) -as:\inputencoding{latin9} + +We now define the monadic DSL type (\lstinline!MyDSL!) and its runner +function (\lstinline!runMyDSL!) as: \begin{lstlisting} type MyDSL[A] = MonadDSL[F, A] def runMyDSL[A]: MyDSL[A] => A = MonadDSL.run(runF) \end{lstlisting} -\inputencoding{utf8}DSL programs may be written using functor blocks:\inputencoding{latin9} +DSL programs may be written using functor blocks: \begin{lstlisting} type FileDSL[A] = MonadDSL[PrgFileC, A] def runFileDSL[A]: FileDSL[A] => A = MonadDSL.run(runnerFile) @@ -879,11 +800,10 @@ \subsection{A first recipe for monadic DSLs\label{subsec:A-first-recipe-monadic- scala> runFileDSL(prgFile1) res0: String = "version = 1" \end{lstlisting} -\inputencoding{utf8} -Note that each DSL operation needs to be wrapped in an \inputencoding{latin9}\lstinline!Op!\inputencoding{utf8} + +Note that each DSL operation needs to be wrapped in an \lstinline!Op! case class. To make the code shorter, we may define an implicit conversion -from \inputencoding{latin9}\lstinline!PrgFileC[A]!\inputencoding{utf8} -to \inputencoding{latin9}\lstinline!Op[A]!\inputencoding{utf8}:\inputencoding{latin9} +from \lstinline!PrgFileC[A]! to \lstinline!Op[A]!: \begin{lstlisting} implicit def toOp[A](p: PrgFileC[A]): Op[A] = Op(p) // *** check if this code actually works val prgFile2: FileDSL[String] = for { // Does it help if we begin the functor block with a Pure()? @@ -894,65 +814,53 @@ \subsection{A first recipe for monadic DSLs\label{subsec:A-first-recipe-monadic- scala> runFileDSL(prgFile1) res1: String = "version = 1" \end{lstlisting} -\inputencoding{utf8} -Does\index{free monad!monad laws} the type constructor \inputencoding{latin9}\lstinline!MonadDSL[F, A]!\inputencoding{utf8} + +Does\index{free monad!monad laws} the type constructor \lstinline!MonadDSL[F, A]! satisfy the laws of the monad? We can quickly find out that it does -\emph{not}. The left identity law of \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -fails:\inputencoding{latin9} +\emph{not}. The left identity law of \lstinline!flatMap! fails: \begin{lstlisting} MonadDSL.pure(a).flatMap(f) == Bind(Val(a))(f) // This should equal f(a) by the left identity law. \end{lstlisting} -\inputencoding{utf8}However, the law will hold \emph{after} we apply a runner function -to both sides. For brevity, let us denote \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}\textsf{'}s -runner function by \inputencoding{latin9}\lstinline!r(x)!\inputencoding{utf8} -instead of \inputencoding{latin9}\lstinline!run(runner)(x)!\inputencoding{utf8}. -The code of \inputencoding{latin9}\lstinline!run!\inputencoding{utf8} -shows that \inputencoding{latin9}\lstinline!r(Bind(p)(f)) == r(f(r(p)))!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!r(Val(a)) == a!\inputencoding{utf8}. -Then we find that both sides of the law give \inputencoding{latin9}\lstinline!r(f(a))!\inputencoding{utf8} -when run:\inputencoding{latin9} +However, the law will hold \emph{after} we apply a runner function +to both sides. For brevity, let us denote \lstinline!MonadDSL!\textsf{'}s +runner function by \lstinline!r(x)! instead of \lstinline!run(runner)(x)!. +The code of \lstinline!run! shows that \lstinline!r(Bind(p)(f)) == r(f(r(p)))! +and \lstinline!r(Val(a)) == a!. Then we find that both sides of the +law give \lstinline!r(f(a))! when run: \begin{lstlisting} r(MonadDSL.pure(a).flatMap(f)) == r(Bind(Val(a))(f)) == r(f(a)) \end{lstlisting} -\inputencoding{utf8} -The associativity law~(\ref{eq:associativity-law-flatMap}) of \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -also does not hold directly for \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -values but will hold after a \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -program has been run. To see that, first compare the two sides of -the law~(\ref{eq:associativity-law-flatMap}):\inputencoding{latin9} + +The associativity law~(\ref{eq:associativity-law-flatMap}) of \lstinline!flatMap! +also does not hold directly for \lstinline!MonadDSL! values but will +hold after a \lstinline!MonadDSL! program has been run. To see that, +first compare the two sides of the law~(\ref{eq:associativity-law-flatMap}): \begin{lstlisting} /* left-hand side: */ p.flatMap(x => f(x).flatMap(g)) == Bind(p)(x => Bind(f(x))(g)) /* right-hand side: */ p.flatMap(f).flatMap(g) == Bind(Bind(p)(f))(g) \end{lstlisting} -\inputencoding{utf8}The law fails since the two data structures are not the same. When -we apply a runner \inputencoding{latin9}\lstinline!r!\inputencoding{utf8} -to both sides, we get:\inputencoding{latin9} +The law fails since the two data structures are not the same. When +we apply a runner \lstinline!r! to both sides, we get: \begin{lstlisting} /* left-hand side: */ r(Bind(p)(x => Bind(f(x))(g))) == r(Bind(f(r(p)))(g)) == r(g(r(f(r(p))))) /* right-hand side: */ r(Bind(Bind(p)(f))(g)) == r(g(r(Bind(p)(f)))) == r(g(r(f(r(p))))) \end{lstlisting} -\inputencoding{utf8} + We may say that failures of the monad laws are \textsf{``}not observable\textsf{''} since the laws will hold after running the DSL programs. -Could we implement \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}\textsf{'}s -\inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} method +Could we implement \lstinline!MonadDSL!\textsf{'}s \lstinline!flatMap! method differently so that the monad laws already hold before applying a runner? To satisfy the left identity law, we need to avoid creating -a nested structure of the form \inputencoding{latin9}\lstinline!Bind(Val(a))!\inputencoding{utf8} -when applying \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -to a value of the form \inputencoding{latin9}\lstinline!Val(a)!\inputencoding{utf8}. -Instead, \inputencoding{latin9}\lstinline!Val(a).flatMap(f)!\inputencoding{utf8} -should return \inputencoding{latin9}\lstinline!f(a)!\inputencoding{utf8} -as required by the left identity law. To satisfy the associativity -law, we need to avoid creating a nested structure of the form \inputencoding{latin9}\lstinline!Bind(Bind(...))!\inputencoding{utf8} -when applying \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -to a \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} value. -Instead, applying \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -to \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} should -directly return the value required by the associativity law. The new -code of \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -is:\inputencoding{latin9} +a nested structure of the form \lstinline!Bind(Val(a))! when applying +\lstinline!flatMap! to a value of the form \lstinline!Val(a)!. Instead, +\lstinline!Val(a).flatMap(f)! should return \lstinline!f(a)! as +required by the left identity law. To satisfy the associativity law, +we need to avoid creating a nested structure of the form \lstinline!Bind(Bind(...))! +when applying \lstinline!flatMap! to a \lstinline!Bind! value. Instead, +applying \lstinline!flatMap! to \lstinline!Bind! should directly +return the value required by the associativity law. The new code of +\lstinline!flatMap! is: \begin{lstlisting} sealed trait MonadDSL[F[_], A] { *** check that this code works def flatMap[B](f: A => MonadDSL[F, B]): MonadDSL[F, B] = this match { @@ -963,25 +871,22 @@ \subsection{A first recipe for monadic DSLs\label{subsec:A-first-recipe-monadic- ... // Other code remains unchanged. } \end{lstlisting} -\inputencoding{utf8}The resulting code creates fewer nested case classes in memory. +The resulting code creates fewer nested case classes in memory. Unlike other laws, the right identity law already holds for any value -\inputencoding{latin9}\lstinline!p!\inputencoding{utf8} of type \inputencoding{latin9}\lstinline!MonadDSL[F, A]!\inputencoding{utf8}:\inputencoding{latin9} +\lstinline!p! of type \lstinline!MonadDSL[F, A]!: \begin{lstlisting} p.flatMap(f andThen MonadDSL.pure) == p.map(f) \end{lstlisting} -\inputencoding{utf8}The reason is that our current code implements \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}\textsf{'}s -\inputencoding{latin9}\lstinline!map!\inputencoding{utf8} method -via \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -in exactly this way. +The reason is that our current code implements \lstinline!MonadDSL!\textsf{'}s +\lstinline!map! method via \lstinline!flatMap! in exactly this way. \subsection{Running a DSL program into another monad} In the code from the previous section, one may use different effect -runners with \emph{the same} function \inputencoding{latin9}\lstinline!MonadDSL.run!\inputencoding{utf8}. -We may apply \inputencoding{latin9}\lstinline!MonadDSL.run!\inputencoding{utf8} -to any effect runner of type $\forall X.\,F^{X}\rightarrow X$ and -obtain a monad runner of type \inputencoding{latin9}\lstinline!MonadDSL[F, A] => A!\inputencoding{utf8}. +runners with \emph{the same} function \lstinline!MonadDSL.run!. We +may apply \lstinline!MonadDSL.run! to any effect runner of type $\forall X.\,F^{X}\rightarrow X$ +and obtain a monad runner of type \lstinline!MonadDSL[F, A] => A!. More generally, we may need to run the $F$-effects into \emph{another monad}. Given an effect runner of type $\forall X.\,F^{X}\rightarrow M^{X}$, @@ -992,28 +897,23 @@ \subsection{Running a DSL program into another monad} that the execution of $F$-effects may produce errors.\footnote{\index{jokes}\textsf{``}\emph{A function that launches real-world missiles can run out of missiles.}\textsf{''} (A quote attributed to \index{Simon Peyton Jones}Simon Peyton Jones, see \texttt{\href{https://stackoverflow.com/questions/2773004}{https://stackoverflow.com/questions/2773004}} -for discussion.)} For instance, the code of \inputencoding{latin9}\lstinline!runFileC!\inputencoding{utf8} -(the runner for the \inputencoding{latin9}\lstinline!PrgFile!\inputencoding{utf8} -DSL) will throw exceptions when files are not found or not readable. -We may catch those exceptions with Scala\textsf{'}s standard \inputencoding{latin9}\lstinline!Try!\inputencoding{utf8} -class. We will then need to replace a runner of type \inputencoding{latin9}\lstinline!PrgFileC[X] => X!\inputencoding{utf8} -by a polymorphic function of type \inputencoding{latin9}\lstinline!PrgFileC[X] => Try[X]!\inputencoding{utf8}. -The new code looks like this:\inputencoding{latin9} +for discussion.)} For instance, the code of \lstinline!runFileC! (the runner for the +\lstinline!PrgFile! DSL) will throw exceptions when files are not +found or not readable. We may catch those exceptions with Scala\textsf{'}s +standard \lstinline!Try! class. We will then need to replace a runner +of type \lstinline!PrgFileC[X] => X! by a polymorphic function of +type \lstinline!PrgFileC[X] => Try[X]!. The new code looks like this: \begin{lstlisting} trait RunnerTry[F[_]] { def run[X]: F[X] => Try[X] } val runnerFileTry = new RunnerTry[PrgFileC] { def run[X]: PrgFileC[X] => Try[X] = p => Try(runFileC(p)) } \end{lstlisting} -\inputencoding{utf8} The code of the \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}\textsf{'}s -runner needs to be modified to accommodate the new type. Since a \inputencoding{latin9}\lstinline!Val(a)!\inputencoding{utf8} -cannot generate errors (only $F$-effects can), we transform a \inputencoding{latin9}\lstinline!Val(a)!\inputencoding{utf8} -into \inputencoding{latin9}\lstinline!Success(a)!\inputencoding{utf8}. -A \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} value -can only result from using \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}\textsf{'}s -\inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8}, and -so it is reasonable to transform \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} -into a call to \inputencoding{latin9}\lstinline!Try!\inputencoding{utf8}\textsf{'}s -\inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} method. -The new runner\textsf{'}s code becomes:\inputencoding{latin9} + The code of the \lstinline!MonadDSL!\textsf{'}s runner needs to be modified +to accommodate the new type. Since a \lstinline!Val(a)! cannot generate +errors (only $F$-effects can), we transform a \lstinline!Val(a)! +into \lstinline!Success(a)!. A \lstinline!Bind! value can only result +from using \lstinline!MonadDSL!\textsf{'}s \lstinline!flatMap!, and so it +is reasonable to transform \lstinline!Bind! into a call to \lstinline!Try!\textsf{'}s +\lstinline!flatMap! method. The new runner\textsf{'}s code becomes: \begin{lstlisting} def runTry[F[_], A](runnerTry: RunnerTry[F]): MonadDSL[F, A] => Try[A] = { case Val(a) => Success(a) @@ -1021,22 +921,16 @@ \subsection{Running a DSL program into another monad} case Op(op) => runner.run(op) } *** check that this code works \end{lstlisting} -\inputencoding{utf8} -Let us generalize the code of \inputencoding{latin9}\lstinline!runTry!\inputencoding{utf8} -to an arbitrary monad $M$ instead of \inputencoding{latin9}\lstinline!Try!\inputencoding{utf8}. -We note that \inputencoding{latin9}\lstinline!runTry!\inputencoding{utf8} -uses only two values specific to \inputencoding{latin9}\lstinline!Try!\inputencoding{utf8}: -the \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -method of the \inputencoding{latin9}\lstinline!Try!\inputencoding{utf8} -monad and the \inputencoding{latin9}\lstinline!Success!\inputencoding{utf8} -case class, which corresponds to the \inputencoding{latin9}\lstinline!Try!\inputencoding{utf8} -monad\textsf{'}s \inputencoding{latin9}\lstinline!pure!\inputencoding{utf8} -method. To adapt the code to another monad $M$ instead of \inputencoding{latin9}\lstinline!Try!\inputencoding{utf8}, -we need to use $M$\textsf{'}s \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -and to replace \inputencoding{latin9}\lstinline!Success!\inputencoding{utf8} -by $M$\textsf{'}s \inputencoding{latin9}\lstinline!pure!\inputencoding{utf8}. -The result is a \textsf{``}universal runner\textsf{''}\index{free monad!universal runner} -for the free monad:\inputencoding{latin9} + +Let us generalize the code of \lstinline!runTry! to an arbitrary +monad $M$ instead of \lstinline!Try!. We note that \lstinline!runTry! +uses only two values specific to \lstinline!Try!: the \lstinline!flatMap! +method of the \lstinline!Try! monad and the \lstinline!Success! +case class, which corresponds to the \lstinline!Try! monad\textsf{'}s \lstinline!pure! +method. To adapt the code to another monad $M$ instead of \lstinline!Try!, +we need to use $M$\textsf{'}s \lstinline!flatMap! and to replace \lstinline!Success! +by $M$\textsf{'}s \lstinline!pure!. The result is a \textsf{``}universal runner\textsf{''}\index{free monad!universal runner} +for the free monad: \begin{lstlisting} trait RunnerM[F[_], M[_]] { def run[X]: F[X] => M[X] } def runM[F[_], M[_]: Monad, A](runnerM: RunnerM[F, M]): MonadDSL[F, A] => M[A] = { @@ -1045,29 +939,22 @@ \subsection{Running a DSL program into another monad} case Op(op) => runnerM.run(op) } *** check that this code works \end{lstlisting} -\inputencoding{utf8} -We have implemented the universal runner by mapping \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}\textsf{'}s -monadic methods (\inputencoding{latin9}\lstinline!pure!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8}) -into the corresponding \inputencoding{latin9}\lstinline!pure!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -methods of the monad $M$. More precisely, if we fix an effect runner -\inputencoding{latin9}\lstinline!runnerM!\inputencoding{utf8} then -the corresponding runner \inputencoding{latin9}\lstinline!run = runM(runnerM)!\inputencoding{utf8} -satisfies:\inputencoding{latin9} + +We have implemented the universal runner by mapping \lstinline!MonadDSL!\textsf{'}s +monadic methods (\lstinline!pure! and \lstinline!flatMap!) into +the corresponding \lstinline!pure! and \lstinline!flatMap! methods +of the monad $M$. More precisely, if we fix an effect runner \lstinline!runnerM! +then the corresponding runner \lstinline!run = runM(runnerM)! satisfies: \begin{lstlisting} run(MonadDSL.pure(a)) == run(Val(a)) == Monad[M].pure(a) run(p.flatMap(f)) == run(Bind(p)(f)) == run(p.flatMap(f andThen run)) \end{lstlisting} -\inputencoding{utf8}These equations also show that there is \emph{only one} implementation -of a runner function of type \inputencoding{latin9}\lstinline!run[A]: MonadDSL[A] => M[A]!\inputencoding{utf8} -that preserves the \inputencoding{latin9}\lstinline!pure!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -operations. The \inputencoding{latin9}\lstinline!Op(op)!\inputencoding{utf8} -values must be transformed into \inputencoding{latin9}\lstinline!runnerM.run(op)!\inputencoding{utf8} -as there is no other way of converting a value of type \inputencoding{latin9}\lstinline!F[A]!\inputencoding{utf8} -into a value of type \inputencoding{latin9}\lstinline!M[A]!\inputencoding{utf8} -with an arbitrary monad \inputencoding{latin9}\lstinline!M!\inputencoding{utf8}. +These equations also show that there is \emph{only one} implementation +of a runner function of type \lstinline!run[A]: MonadDSL[A] => M[A]! +that preserves the \lstinline!pure! and \lstinline!flatMap! operations. +The \lstinline!Op(op)! values must be transformed into \lstinline!runnerM.run(op)! +as there is no other way of converting a value of type \lstinline!F[A]! +into a value of type \lstinline!M[A]! with an arbitrary monad \lstinline!M!. The use of universal runners (with an arbitrary monad $M$) allows the programmer to better separate the business logic of an application @@ -1080,50 +967,41 @@ \section{Different encodings of the free monad} \subsection{Motivation\label{subsec:Motivation-free-monad-different-encodings}} -The type \inputencoding{latin9}\lstinline!MonadDSL[F, A]!\inputencoding{utf8} -is called a \textbf{free monad} on\index{free monad} the type constructor -\inputencoding{latin9}\lstinline!F!\inputencoding{utf8}. The word -\textsf{``}free\textsf{''} indicates that \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -is free of any domain-specific code. We may view \inputencoding{latin9}\lstinline!MonadDSL[F, A]!\inputencoding{utf8} -as a wrapper that adds the monad\textsf{'}s functionality to any type constructor -\inputencoding{latin9}\lstinline!F!\inputencoding{utf8} and creates -a new monadic DSL based on \inputencoding{latin9}\lstinline!F!\inputencoding{utf8}\textsf{'}s -operations. The construction uses \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -as a type parameter, and so it works equally well with every \inputencoding{latin9}\lstinline!F!\inputencoding{utf8}. - -A monad must support the methods \inputencoding{latin9}\lstinline!pure!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8}, and -\inputencoding{latin9}\lstinline!map!\inputencoding{utf8}. The effect -constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -often does not have these methods. The \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -constructor is able to convert an arbitrary \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -into a monad because \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -defines the methods \inputencoding{latin9}\lstinline!pure!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8}, and -\inputencoding{latin9}\lstinline!map!\inputencoding{utf8} in a special -way. Those methods create some data structures (nested case classes) -in memory but do not perform any domain-specific effects or actions. -The effects will be performed at a later time by an effect runner -(\inputencoding{latin9}\lstinline!runF!\inputencoding{utf8} of type -\inputencoding{latin9}\lstinline!F[A] => A!\inputencoding{utf8}) -when we apply \inputencoding{latin9}\lstinline!MonadDSL.run!\inputencoding{utf8} -to a DSL program. Effect runners are not part of the code of \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -and will need to be supplied separately. So, we may run the same \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} +The type \lstinline!MonadDSL[F, A]! is called a \textbf{free monad} +on\index{free monad} the type constructor \lstinline!F!. The word +\textsf{``}free\textsf{''} indicates that \lstinline!MonadDSL! is free of any domain-specific +code. We may view \lstinline!MonadDSL[F, A]! as a wrapper that adds +the monad\textsf{'}s functionality to any type constructor \lstinline!F! and +creates a new monadic DSL based on \lstinline!F!\textsf{'}s operations. The +construction uses \lstinline!F! as a type parameter, and so it works +equally well with every \lstinline!F!. + +A monad must support the methods \lstinline!pure!, \lstinline!flatMap!, +and \lstinline!map!. The effect constructor \lstinline!F! often +does not have these methods. The \lstinline!MonadDSL! constructor +is able to convert an arbitrary \lstinline!F! into a monad because +\lstinline!MonadDSL! defines the methods \lstinline!pure!, \lstinline!flatMap!, +and \lstinline!map! in a special way. Those methods create some data +structures (nested case classes) in memory but do not perform any +domain-specific effects or actions. The effects will be performed +at a later time by an effect runner (\lstinline!runF! of type \lstinline!F[A] => A!) +when we apply \lstinline!MonadDSL.run! to a DSL program. Effect runners +are not part of the code of \lstinline!MonadDSL! and will need to +be supplied separately. So, we may run the same \lstinline!MonadDSL! program using different runners. -The code of \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -satisfies our goal of producing a type-safe DSL out of a given set -of domain-specific operations. But it turns out that \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -is not the only way of turning a given type constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -into a monad. Various presentations and blog posts about the free -monad have used different implementations that are not obviously equivalent -to each other. Here are three examples that we will call \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8}, and -\inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8}. +The code of \lstinline!MonadDSL! satisfies our goal of producing +a type-safe DSL out of a given set of domain-specific operations. +But it turns out that \lstinline!MonadDSL! is not the only way of +turning a given type constructor \lstinline!F! into a monad. Various +presentations and blog posts about the free monad have used different +implementations that are not obviously equivalent to each other. Here +are three examples that we will call \lstinline!Free1!, \lstinline!Free2!, +and \lstinline!Free3!. In a 2012 blog post,\footnote{See \texttt{\href{http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html}{http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html}}} \index{Gabriella Gonzalez (known as Gabriel Gonzalez prior to 2021)}G.~Gonzalez -showed a free monad corresponding to this Scala code:\inputencoding{latin9} +showed a free monad corresponding to this Scala code: \begin{lstlisting} abstract class Free1[F[_]: Functor, T] { def flatMap[A](f: T => Free1[F, A]): Free1[F, A] = this match { @@ -1134,10 +1012,10 @@ \subsection{Motivation\label{subsec:Motivation-free-monad-different-encodings}} final case class Pure[F[_], T](t: T) extends Free1[F, T] final case class Flatten[F[_], T](p: F[Free1[F, T]]) extends Free1[F, T] \end{lstlisting} -\inputencoding{utf8} + In 2014,\footnote{See \texttt{\href{http://functionaltalks.org/2014/11/23/runar-oli-bjarnason-free-monad/}{http://functionaltalks.org/2014/11/23/runar-oli-bjarnason-free-monad/}}} R.~Bjarnason\index{Runar@R\'unar Bjarnason} presented the following -implementation of a free monad:\inputencoding{latin9} +implementation of a free monad: \begin{lstlisting} sealed trait Free2[F[_], T] { def flatMap[A](f: T => Free2[F, A]): Free2[F, A] = this match { @@ -1148,9 +1026,9 @@ \subsection{Motivation\label{subsec:Motivation-free-monad-different-encodings}} final case class Return[F[_], T](t: T) extends Free2[F, T] final case class Bind[F[_], T, A](p: F[A], g: A => Free2[F, T]) extends Free2[F, T] \end{lstlisting} -\inputencoding{utf8} + In 2016, K.~Robinson\index{Kelley Robinson} gave a talk\footnote{See \texttt{\href{https://www.slideshare.net/KelleyRobinson1/why-the-free-monad-isnt-free-61836547}{https://www.slideshare.net/KelleyRobinson1/why-the-free-monad-isnt-free-61836547}}} -where the code for the free monad looked like this:\inputencoding{latin9} +where the code for the free monad looked like this: \begin{lstlisting} sealed trait Free3[F[_], T] { def flatMap[A](f: T => Free3[F, A]): Free3[F, A] = FlatMap(this, f) @@ -1159,32 +1037,23 @@ \subsection{Motivation\label{subsec:Motivation-free-monad-different-encodings}} final case class Suspend[F[_], T](f: F[T]) extends Free3[F, T] final case class FlatMap[F[_], T, A](p: Free3[F, A], g: A => Free3[F, T]) extends Free3[F, T] \end{lstlisting} -\inputencoding{utf8} -The main differences between \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8}, and -\inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} are in -the definitions of the case classes and of the \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -functions, so we omitted all other code. The type of \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} -is the same as \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -except for renaming \inputencoding{latin9}\lstinline!Pure!\inputencoding{utf8} -to \inputencoding{latin9}\lstinline!Val!\inputencoding{utf8}, \inputencoding{latin9}\lstinline!Suspend!\inputencoding{utf8} -to \inputencoding{latin9}\lstinline!Op!\inputencoding{utf8}, and -\inputencoding{latin9}\lstinline!FlatMap(f, g)!\inputencoding{utf8} -to \inputencoding{latin9}\lstinline!Bind(f)(g)!\inputencoding{utf8}. -However, \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8} have -different data in their case classes and are not obviously equivalent -to each other (or to \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8}). -We call these implementations \textbf{encodings}\index{free monad!encodings} + +The main differences between \lstinline!Free1!, \lstinline!Free2!, +and \lstinline!Free3! are in the definitions of the case classes +and of the \lstinline!flatMap! functions, so we omitted all other +code. The type of \lstinline!Free3! is the same as \lstinline!MonadDSL! +except for renaming \lstinline!Pure! to \lstinline!Val!, \lstinline!Suspend! +to \lstinline!Op!, and \lstinline!FlatMap(f, g)! to \lstinline!Bind(f)(g)!. +However, \lstinline!Free1! and \lstinline!Free2! have different +data in their case classes and are not obviously equivalent to each +other (or to \lstinline!Free3!). We call these implementations \textbf{encodings}\index{free monad!encodings} of the free monad. The different codes implement the same idea (turning -a type constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -into a monad) in different ways. +a type constructor \lstinline!F! into a monad) in different ways. To figure out how to use those codes in practice, a programmer might ask the following questions: Are the three encodings equivalent? What laws does their code need to satisfy in order to be considered \textsf{``}correct\textsf{''}? -The \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8} type -has 2 case classes and \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} +The \lstinline!Free2! type has 2 case classes and \lstinline!Free3! has 3; are there any other encodings of the free monad? Is there a systematic way of finding constructions that convert any type into a \textsf{``}free functor\textsf{''}, a \textsf{``}free monoid\textsf{''}, or into another \textsf{``}free\textsf{''} @@ -1194,24 +1063,19 @@ \subsection{Motivation\label{subsec:Motivation-free-monad-different-encodings}} \subsection{The raw tree encoding} To understand the relationships between the encodings of the free -monad, let us first examine how the code of \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} -(or equivalently \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8}) -implements the \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -method. In \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}, -we implemented \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -through \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8}. -As a result, our code for the \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} +monad, let us first examine how the code of \lstinline!MonadDSL! +(or equivalently \lstinline!Free3!) implements the \lstinline!map! +method. In \lstinline!MonadDSL!, we implemented \lstinline!map! +through \lstinline!flatMap!. As a result, our code for the \lstinline!map! method is not similar to the code of the two other monadic methods -(\inputencoding{latin9}\lstinline!pure!\inputencoding{utf8} and \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8}) -that merely create new values of the case classes \inputencoding{latin9}\lstinline!Val!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} without -performing any computations. +(\lstinline!pure! and \lstinline!flatMap!) that merely create new +values of the case classes \lstinline!Val! and \lstinline!Bind! +without performing any computations. -We could implement \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -in a similar way if we added a new case class, say \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8}, -to \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}. -The result is a new encoding of the free monad that we will call \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} -(since it uses $4$ case classes):\inputencoding{latin9} +We could implement \lstinline!map! in a similar way if we added a +new case class, say \lstinline!FMap!, to \lstinline!MonadDSL!. The +result is a new encoding of the free monad that we will call \lstinline!Free4! +(since it uses $4$ case classes): \begin{lstlisting} sealed trait Free4[F[_], A] { def flatMap[B](f: A => Free4[F, B]): Free4[F, B] = Bind(this)(f) @@ -1225,7 +1089,7 @@ \subsection{The raw tree encoding} final case class FMap[F[_], A, B](pa: Free4[F, B])(val f: B => A) extends Free4[F, A] final case class Op[F[_], A](op: F[A]) extends Free4[F, A] // Wrap all domain-specific operations. \end{lstlisting} -\inputencoding{utf8}The code of the runner needs to be revised accordingly:\inputencoding{latin9} +The code of the runner needs to be revised accordingly: \begin{lstlisting} def run[F[_], A](runner: Runner[F]): Free4[F, A] => A = { case Val(a) => a @@ -1234,68 +1098,53 @@ \subsection{The raw tree encoding} case Op(op) => runner.run(op) } *** check that this code works \end{lstlisting} -\inputencoding{utf8} -The code of \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8}\textsf{'}s -methods \inputencoding{latin9}\lstinline!map!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8}, and -\inputencoding{latin9}\lstinline!pure!\inputencoding{utf8} merely -wraps the arguments of each method into a case class without performing -any computations with those arguments. We call this implementation -of the free monad the \textbf{raw tree encoding}\index{free monad!raw tree encoding}. -The name means that a DSL program is a fully unevaluated (or \textsf{``}raw\textsf{''}) -expression tree that creates a new nested case class for each step -of the required computations. + +The code of \lstinline!Free4!\textsf{'}s methods \lstinline!map!, \lstinline!flatMap!, +and \lstinline!pure! merely wraps the arguments of each method into +a case class without performing any computations with those arguments. +We call this implementation of the free monad the \textbf{raw tree +encoding}\index{free monad!raw tree encoding}. The name means that +a DSL program is a fully unevaluated (or \textsf{``}raw\textsf{''}) expression tree +that creates a new nested case class for each step of the required +computations. \subsection{Deriving reduced encodings of the free monad} -In the raw tree encoding (\inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8}), -\emph{none} of the monad laws hold. This is not a problem in practice -because the monad laws will hold after running a DSL program. However, -we notice that the \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} -encoding (which is the same as \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8} +In the raw tree encoding (\lstinline!Free4!), \emph{none} of the +monad laws hold. This is not a problem in practice because the monad +laws will hold after running a DSL program. However, we notice that +the \lstinline!Free3! encoding (which is the same as \lstinline!MonadDSL! from Section~\ref{subsec:Stage-5:-refactoring-monadDSL}) satisfies the monad\textsf{'}s right identity law by design. A byproduct of that design -is that \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} -has fewer case classes than \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8}. -We also notice that the code of \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8} -uses just $2$ case classes and satisfies all monad laws. +is that \lstinline!Free3! has fewer case classes than \lstinline!Free4!. +We also notice that the code of \lstinline!Free2! uses just $2$ +case classes and satisfies all monad laws. These observations suggest that we might find an encoding with fewer case classes (which we call a \textbf{reduced encoding}) if we use the monad laws when designing the free monad\textsf{'}s type constructor. To -see how this works, we will first reduce \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} -to \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} and -then \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} -to \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8} by -imposing certain monad laws directly on the DSL program values. These -derivations will also show that \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8}, and -\inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} are equivalent -to each other in their expressive power.\footnote{The \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8} encoding -cannot be derived from the others because \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8} -\emph{requires} that the effect constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -be a functor with a \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -method, while the other encodings work for any \inputencoding{latin9}\lstinline!F!\inputencoding{utf8}. -Later in this chapter, we will prove that \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8} -is equivalent to \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8} -when \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} is a -functor. We will also show more examples of free typeclass constructions -that require another typeclass.} - -To derive \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} -from \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8}, -we use the right identity law of \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -to express \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -through \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8}:\inputencoding{latin9} +see how this works, we will first reduce \lstinline!Free4! to \lstinline!Free3! +and then \lstinline!Free3! to \lstinline!Free2! by imposing certain +monad laws directly on the DSL program values. These derivations will +also show that \lstinline!Free2!, \lstinline!Free3!, and \lstinline!Free4! +are equivalent to each other in their expressive power.\footnote{The \lstinline!Free1! encoding cannot be derived from the others +because \lstinline!Free1! \emph{requires} that the effect constructor +\lstinline!F! be a functor with a \lstinline!map! method, while +the other encodings work for any \lstinline!F!. Later in this chapter, +we will prove that \lstinline!Free1! is equivalent to \lstinline!Free2! +when \lstinline!F! is a functor. We will also show more examples +of free typeclass constructions that require another typeclass.} + +To derive \lstinline!Free3! from \lstinline!Free4!, we use the right +identity law of \lstinline!flatMap! to express \lstinline!map! through +\lstinline!flatMap!: \begin{lstlisting} p.map(f) == p.flatMap (f andThen pure) \end{lstlisting} -\inputencoding{utf8}As we have already seen, this definition makes the right identity -law hold for \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8}. -Following the form of that law, we write a function for transforming -a \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} program -into a \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} -program:\inputencoding{latin9} +As we have already seen, this definition makes the right identity +law hold for \lstinline!Free3!. Following the form of that law, we +write a function for transforming a \lstinline!Free4! program into +a \lstinline!Free3! program: \begin{lstlisting} def free4toFree3[F[_], A]: Free4[F, A] => Free3[F, A] = { case fmap @ FMap(p) => Bind(free4toFree3(p))(fmap.f andThen Free3.pure) // Replace FMap by Bind. @@ -1304,9 +1153,8 @@ \subsection{Deriving reduced encodings of the free monad} case Op(op) => Op(op) } \end{lstlisting} -\inputencoding{utf8}An inverse transformation (from \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} -to \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8}) is -implemented by translating all case classes identically:\inputencoding{latin9} +An inverse transformation (from \lstinline!Free3! to \lstinline!Free4!) +is implemented by translating all case classes identically: \begin{lstlisting} def free3toFree4[F[_], A]: Free3[F, A] => Free4[F, A] = { case Val(a) => Val(a) @@ -1314,121 +1162,89 @@ \subsection{Deriving reduced encodings of the free monad} case Op(op) => Op(op) } \end{lstlisting} -\inputencoding{utf8}The composition \inputencoding{latin9}\lstinline!free3toFree4 andThen free4toFree3!\inputencoding{utf8} -is an identity transformation because each case class is simply copied +The composition \lstinline!free3toFree4 andThen free4toFree3! is +an identity transformation because each case class is simply copied over to the other type. However, the composition in the other order, -\inputencoding{latin9}\lstinline!free4toFree3 andThen free3toFree4!\inputencoding{utf8}, -is not an identity since \inputencoding{latin9}\lstinline!free3toFree4!\inputencoding{utf8} -never creates \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} -values of type \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8}. -It is impossible to convert any values of type \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} -to \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8} because -it is impossible to determine whether a given function of type \inputencoding{latin9}\lstinline!A => Free3[B]!\inputencoding{utf8} -is expressible in the form \inputencoding{latin9}\lstinline!(f andThen pure)!\inputencoding{utf8} -with some \inputencoding{latin9}\lstinline!f: A => B!\inputencoding{utf8}. - -It follows that \inputencoding{latin9}\lstinline!free3toFree4!\inputencoding{utf8} -is injective while \inputencoding{latin9}\lstinline!free4toFree3!\inputencoding{utf8} -is surjective. In this sense, the encoding \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} -is \textsf{``}larger\textsf{''} than \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8}. -However, \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} -does not express any more functionality than \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8}. -After running the corresponding \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} DSL -programs, the results will be the same:\inputencoding{latin9} +\lstinline!free4toFree3 andThen free3toFree4!, is not an identity +since \lstinline!free3toFree4! never creates \lstinline!Free4! values +of type \lstinline!FMap!. It is impossible to convert any values +of type \lstinline!Bind! to \lstinline!FMap! because it is impossible +to determine whether a given function of type \lstinline!A => Free3[B]! +is expressible in the form \lstinline!(f andThen pure)! with some +\lstinline!f: A => B!. + +It follows that \lstinline!free3toFree4! is injective while \lstinline!free4toFree3! +is surjective. In this sense, the encoding \lstinline!Free4! is \textsf{``}larger\textsf{''} +than \lstinline!Free3!. However, \lstinline!Free4! does not express +any more functionality than \lstinline!Free3!. After running the +corresponding \lstinline!Free3! and \lstinline!Free4! DSL programs, +the results will be the same: \begin{lstlisting} runFree4(runner)(free4program) == runFree3(runner)(free4toFree3(free4program)) runFree3(runner)(free3program) == runFree4(runner)(free3toFree4(free3program)) \end{lstlisting} -\inputencoding{utf8}To verify this, we first note that the case classes \inputencoding{latin9}\lstinline!Val!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8}, and \inputencoding{latin9}\lstinline!Op!\inputencoding{utf8} -within the types \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} will -be evaluated in exactly the same way by both runners. So, DSL programs -in \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} and -\inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} containing -those case classes will give the same results when run. +To verify this, we first note that the case classes \lstinline!Val!, +\lstinline!Bind!, and \lstinline!Op! within the types \lstinline!Free3! +and \lstinline!Free4! will be evaluated in exactly the same way by +both runners. So, DSL programs in \lstinline!Free3! and \lstinline!Free4! +containing those case classes will give the same results when run. It remains to verify that the runners will produce the same results -for a \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} -program containing an \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8} -case class and for the corresponding \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} -program. Running a \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} -program of the form \inputencoding{latin9}\lstinline!p = FMap(q)(f)!\inputencoding{utf8}, -we will get:\inputencoding{latin9} +for a \lstinline!Free4! program containing an \lstinline!FMap! case +class and for the corresponding \lstinline!Free3! program. Running +a \lstinline!Free4! program of the form \lstinline!p = FMap(q)(f)!, +we will get: \begin{lstlisting} runFree4(runner)(p) == runFree4(runner)(FMap(q)(f)) == f(runFree4(runner)(q)) \end{lstlisting} -\inputencoding{utf8}The \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8} program -corresponding to \inputencoding{latin9}\lstinline!p!\inputencoding{utf8} -is:\inputencoding{latin9} +The \lstinline!Free3! program corresponding to \lstinline!p! is: \begin{lstlisting} free4toFree3(p) == Bind(q)(f andThen Free3.pure) \end{lstlisting} -\inputencoding{utf8}Running the program \inputencoding{latin9}\lstinline!free4toFree3(p)!\inputencoding{utf8} -with \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8}\textsf{'}s -runner will give:\inputencoding{latin9} +Running the program \lstinline!free4toFree3(p)! with \lstinline!Free3!\textsf{'}s +runner will give: \begin{lstlisting} runFree3(runner)(free4toFree3(p)) == runFree3(runner)(Bind(free4toFree3(q))(f andThen Free3.pure)) == runFree3(runner)(Free3.pure(f(runFree3(runner)(free4toFree3(q))))) == runFree3(runner)(Pure(f(runFree3(runner)(free4toFree3(q))))) == f(runFree3(runner)(free4toFree3(q)))) \end{lstlisting} -\inputencoding{utf8}The last expression differs from the result of evaluating \inputencoding{latin9}\lstinline!runFree4(runner)(p)!\inputencoding{utf8} -only in replacing \inputencoding{latin9}\lstinline!runFree4!\inputencoding{utf8} -by \inputencoding{latin9}\lstinline!runFree3!\inputencoding{utf8}. -So, it remains to show that:\inputencoding{latin9} +The last expression differs from the result of evaluating \lstinline!runFree4(runner)(p)! +only in replacing \lstinline!runFree4! by \lstinline!runFree3!. +So, it remains to show that: \begin{lstlisting} f(runFree4(runner)(q)) == f(runFree3(runner)(free4toFree3(q)))) \end{lstlisting} -\inputencoding{utf8}Note that \inputencoding{latin9}\lstinline!q!\inputencoding{utf8} -is a \emph{smaller} monadic program (contains fewer case classes) -than \inputencoding{latin9}\lstinline!p!\inputencoding{utf8}. If -\inputencoding{latin9}\lstinline!q!\inputencoding{utf8} contains -case classes other than \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8}, -we already showed that \inputencoding{latin9}\lstinline!runFree4!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!runFree3!\inputencoding{utf8} -give the same results. If \inputencoding{latin9}\lstinline!q!\inputencoding{utf8} -again contains the \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8} -case class, we will use the inductive assumption that \inputencoding{latin9}\lstinline!runFree4!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!runFree3!\inputencoding{utf8} -give the same results when evaluating smaller monadic programs. - -Let us now derive \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8} -from \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8}. -A conversion function \inputencoding{latin9}\lstinline!free2toFree3!\inputencoding{utf8} -needs to replace \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8}\textsf{'}s -case classes (\inputencoding{latin9}\lstinline!Return!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8}) by -\inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8}\textsf{'}s case -classes (\inputencoding{latin9}\lstinline!Pure!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!Suspend!\inputencoding{utf8}, and -\inputencoding{latin9}\lstinline!FlatMap!\inputencoding{utf8}). The -\inputencoding{latin9}\lstinline!Return!\inputencoding{utf8} case -class corresponds to \inputencoding{latin9}\lstinline!Pure!\inputencoding{utf8}. -Trying to replace \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} -by \inputencoding{latin9}\lstinline!FlatMap!\inputencoding{utf8}, -we find that the type of their data are not the same: the first part -of \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8} has -type \inputencoding{latin9}\lstinline!F[A]!\inputencoding{utf8} while -the first part of \inputencoding{latin9}\lstinline!FlatMap!\inputencoding{utf8} -has type \inputencoding{latin9}\lstinline!Free3[F, A]!\inputencoding{utf8}. -We note that \inputencoding{latin9}\lstinline!Suspend!\inputencoding{utf8} -converts \inputencoding{latin9}\lstinline!F[A]!\inputencoding{utf8} -into \inputencoding{latin9}\lstinline!Free3[F, A]!\inputencoding{utf8}. -So, we write code like this:\inputencoding{latin9} +Note that \lstinline!q! is a \emph{smaller} monadic program (contains +fewer case classes) than \lstinline!p!. If \lstinline!q! contains +case classes other than \lstinline!FMap!, we already showed that +\lstinline!runFree4! and \lstinline!runFree3! give the same results. +If \lstinline!q! again contains the \lstinline!FMap! case class, +we will use the inductive assumption that \lstinline!runFree4! and +\lstinline!runFree3! give the same results when evaluating smaller +monadic programs. + +Let us now derive \lstinline!Free2! from \lstinline!Free3!. A conversion +function \lstinline!free2toFree3! needs to replace \lstinline!Free2!\textsf{'}s +case classes (\lstinline!Return! and \lstinline!Bind!) by \lstinline!Free3!\textsf{'}s +case classes (\lstinline!Pure!, \lstinline!Suspend!, and \lstinline!FlatMap!). +The \lstinline!Return! case class corresponds to \lstinline!Pure!. +Trying to replace \lstinline!Bind! by \lstinline!FlatMap!, we find +that the type of their data are not the same: the first part of \lstinline!Bind! +has type \lstinline!F[A]! while the first part of \lstinline!FlatMap! +has type \lstinline!Free3[F, A]!. We note that \lstinline!Suspend! +converts \lstinline!F[A]! into \lstinline!Free3[F, A]!. So, we write +code like this: \begin{lstlisting} def free2toFree3[F[_], A]: Free2[F, A] => Free3[F, A] = { case Return(a) => Pure(a) case Bind(p, g) => FlatMap(Suspend(p), g andThen free2toFree3) } \end{lstlisting} -\inputencoding{utf8} -The inverse conversion function (\inputencoding{latin9}\lstinline!free3toFree2!\inputencoding{utf8}) -is more complicated because \inputencoding{latin9}\lstinline!Suspend!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!FlatMap!\inputencoding{utf8} -are not straightforwardly mapped into \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8}\textsf{'}s -case classes.{*}{*}{*}\inputencoding{latin9} + +The inverse conversion function (\lstinline!free3toFree2!) is more +complicated because \lstinline!Suspend! and \lstinline!FlatMap! +are not straightforwardly mapped into \lstinline!Free2!\textsf{'}s case classes.{*}{*}{*} \begin{lstlisting} def free3toFree2[F[_], A]: Free3[F, A] => Free2[F, A] = { case Pure(a) => Return(a) @@ -1436,21 +1252,20 @@ \subsection{Deriving reduced encodings of the free monad} case FlatMap(p, g) => ??? } \end{lstlisting} -\inputencoding{utf8}{*}{*}{*}Some of these conversions are injective. +{*}{*}{*}Some of these conversions are injective. \subsection{Types with existential quantifiers} The previous section showed all derivations in the Scala code syntax rather than in the code notation. The reason is that the constructions -\inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8}, \inputencoding{latin9}\lstinline!Free3!\inputencoding{utf8}, -and \inputencoding{latin9}\lstinline!Free4!\inputencoding{utf8} involve +\lstinline!Free2!, \lstinline!Free3!, and \lstinline!Free4! involve a special use of type parameters that is not supported by the type and code notations shown in this book so far. The missing feature is types with an \textbf{existential quantifier}\index{existential quantifier (exists)@existential quantifier ($\exists$)}, which is denoted by the symbol $\exists$ (pronounced \textsf{``}exists\textsf{''}). To clarify the usage of the symbol $\exists$, we begin by recalling -the definition of the type \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}:\inputencoding{latin9} +the definition of the type \lstinline!MonadDSL!: \begin{lstlisting} sealed trait MonadDSL[F[_], T] { def flatMap[A](...) = ... @@ -1460,69 +1275,55 @@ \subsection{Types with existential quantifiers} final case class Op[F[_], T](f: F[T]) extends MonadDSL[F, T] final case class Bind[F[_], T, A](p: MonadDSL[F, A])(g: A => MonadDSL[F, T]) extends MonadDSL[F, T] \end{lstlisting} -\inputencoding{utf8}The case class \inputencoding{latin9}\lstinline!Bind[F, T, A]!\inputencoding{utf8} -uses the type parameter \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -in a special way. Whenever a value of type \inputencoding{latin9}\lstinline!Bind[F, T, A]!\inputencoding{utf8} -is constructed, the type parameter \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -must be set to a specific type. However, the result will be a value -of type \inputencoding{latin9}\lstinline!MonadDSL[F, T]!\inputencoding{utf8}, -which is \emph{not} parameterized by \inputencoding{latin9}\lstinline!A!\inputencoding{utf8}. -The extra type parameter (\inputencoding{latin9}\lstinline!A!\inputencoding{utf8}) -is hidden inside the constructed value of type \inputencoding{latin9}\lstinline!Bind[F, T, A]!\inputencoding{utf8}. +The case class \lstinline!Bind[F, T, A]! uses the type parameter +\lstinline!A! in a special way. Whenever a value of type \lstinline!Bind[F, T, A]! +is constructed, the type parameter \lstinline!A! must be set to a +specific type. However, the result will be a value of type \lstinline!MonadDSL[F, T]!, +which is \emph{not} parameterized by \lstinline!A!. The extra type +parameter (\lstinline!A!) is hidden inside the constructed value +of type \lstinline!Bind[F, T, A]!. -To see this in a code example, let us apply \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -to a given value \inputencoding{latin9}\lstinline!p!\inputencoding{utf8} -of type \inputencoding{latin9}\lstinline!MonadDSL[F, Int]!\inputencoding{utf8}:\inputencoding{latin9} +To see this in a code example, let us apply \lstinline!map! to a +given value \lstinline!p! of type \lstinline!MonadDSL[F, Int]!: \begin{lstlisting} val p: MonadDSL[F, Int] = ... val q: MonadDSL[F, String] = p.map { i: Int => s"value = $i" } // Creates a Bind[F, String, Int]. \end{lstlisting} -\inputencoding{utf8}The value \inputencoding{latin9}\lstinline!q!\inputencoding{utf8} -is declared to be of type \inputencoding{latin9}\lstinline!MonadDSL[F, T]!\inputencoding{utf8} -with \inputencoding{latin9}\lstinline!T = String!\inputencoding{utf8}. -However, the actual data structure in \inputencoding{latin9}\lstinline!q!\inputencoding{utf8} -contains a value of type \inputencoding{latin9}\lstinline!Bind[F, T, A]!\inputencoding{utf8} -with a type parameter \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -chosen as \inputencoding{latin9}\lstinline!A = Int!\inputencoding{utf8}. -The type declaration \inputencoding{latin9}\lstinline!Bind[F, T, A](...) extends MonadDSL[F, T]!\inputencoding{utf8} -hides the type parameter \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -from the rest of the code, although that type parameter will be needed -when constructing a value \inputencoding{latin9}\lstinline!q!\inputencoding{utf8} -of type \inputencoding{latin9}\lstinline!Bind!\inputencoding{utf8}. -This usage of a type parameter is called an \textbf{existentially -quantified} type and is denoted by $\exists A$. - -The other type parameters (\inputencoding{latin9}\lstinline!F!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!T!\inputencoding{utf8}) in \inputencoding{latin9}\lstinline!Bind[F, T, A]!\inputencoding{utf8} +The value \lstinline!q! is declared to be of type \lstinline!MonadDSL[F, T]! +with \lstinline!T = String!. However, the actual data structure in +\lstinline!q! contains a value of type \lstinline!Bind[F, T, A]! +with a type parameter \lstinline!A! chosen as \lstinline!A = Int!. +The type declaration \lstinline!Bind[F, T, A](...) extends MonadDSL[F, T]! +hides the type parameter \lstinline!A! from the rest of the code, +although that type parameter will be needed when constructing a value +\lstinline!q! of type \lstinline!Bind!. This usage of a type parameter +is called an \textbf{existentially quantified} type and is denoted +by $\exists A$. + +The other type parameters (\lstinline!F!, \lstinline!T!) in \lstinline!Bind[F, T, A]! are not existentially quantified because they appear both in the case -class and in the parent trait. So, we write the notation for \inputencoding{latin9}\lstinline!Bind[F, T, A]!\inputencoding{utf8} +class and in the parent trait. So, we write the notation for \lstinline!Bind[F, T, A]! as: \[ \exists A.\,\text{Bind}^{F,T,A}\triangleq\exists A.\,\text{MonadDSL}^{F,A}\times(A\rightarrow\text{MonadDSL}^{F,T})\quad. \] -Adding the type notation for the case classes \inputencoding{latin9}\lstinline!Val!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Op!\inputencoding{utf8}, we -get a definition of the type \inputencoding{latin9}\lstinline!MonadDSL!\inputencoding{utf8}: +Adding the type notation for the case classes \lstinline!Val! and +\lstinline!Op!, we get a definition of the type \lstinline!MonadDSL!: \begin{align*} \text{MonadDSL}^{F,T} & \triangleq\text{Val}^{F,T}+\text{Op}^{F,T}+\exists A.\,\text{Bind}^{F,T,A}\\ & \quad\quad=T+F^{T}+\exists A.\,\text{MonadDSL}^{F,A}\times(A\rightarrow\text{MonadDSL}^{F,T})\quad. \end{align*} To interpret the name \textsf{``}existential\textsf{''}, we may imagine that a type -parameter \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -\textsf{``}still exists\textsf{''} inside the value \inputencoding{latin9}\lstinline!q: MonadDSL[F, T]!\inputencoding{utf8}, -since \inputencoding{latin9}\lstinline!q!\inputencoding{utf8} must -have been created as \inputencoding{latin9}\lstinline!Bind[F, T, A](...)(...)!\inputencoding{utf8} -with a specific (somehow chosen) type \inputencoding{latin9}\lstinline!A!\inputencoding{utf8}. -However, we cannot inspect or obtain the type \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -if we only have the value \inputencoding{latin9}\lstinline!q!\inputencoding{utf8}. -No pattern-matching or other operations on \inputencoding{latin9}\lstinline!q!\inputencoding{utf8} -can determine whether the type \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -is, say, \inputencoding{latin9}\lstinline!Int!\inputencoding{utf8} -or \inputencoding{latin9}\lstinline!String!\inputencoding{utf8}. -We can use the value \inputencoding{latin9}\lstinline!q!\inputencoding{utf8} -in our code as long as we treat \inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -as an unknown, arbitrary, but fixed type. +parameter \lstinline!A! \textsf{``}still exists\textsf{''} inside the value \lstinline!q: MonadDSL[F, T]!, +since \lstinline!q! must have been created as \lstinline!Bind[F, T, A](...)(...)! +with a specific (somehow chosen) type \lstinline!A!. However, we +cannot inspect or obtain the type \lstinline!A! if we only have the +value \lstinline!q!. No pattern-matching or other operations on \lstinline!q! +can determine whether the type \lstinline!A! is, say, \lstinline!Int! +or \lstinline!String!. We can use the value \lstinline!q! in our +code as long as we treat \lstinline!A! as an unknown, arbitrary, +but fixed type. Scala gives two ways of defining types with existential quantifiers: via case classes with an extra type parameter, or via a class with @@ -1536,25 +1337,24 @@ \subsection{Types with existential quantifiers} An example of such a type expression is $\exists C.\,A\times C\times\left(C\rightarrow B\right)$. Let us implement this type via case classes and via type members. -The code using case classes may look like this:\inputencoding{latin9} +The code using case classes may look like this: \begin{lstlisting} sealed trait L[A, B] final case class L1[A, B, C](a: A, c: C, p: C => B) extends L[A, B] \end{lstlisting} -\inputencoding{utf8} -The type notation for \inputencoding{latin9}\lstinline!L[A, B]!\inputencoding{utf8} -is: + +The type notation for \lstinline!L[A, B]! is: \[ L^{A,B}\triangleq\exists C.\,\text{L1}^{A,B,C}=\exists C.\,A\times C\times(C\rightarrow B)\quad. \] -When we create a value of type \inputencoding{latin9}\lstinline!L[A, B]!\inputencoding{utf8}, -we need to assign the type $C$:\inputencoding{latin9} +When we create a value of type \lstinline!L[A, B]!, we need to assign +the type $C$: \begin{lstlisting} val q1: L[Int, Int] = L1[Int, Int, String](1, "abc", _.length) \end{lstlisting} -\inputencoding{utf8}External code may use data of type \inputencoding{latin9}\lstinline!L[A, B]!\inputencoding{utf8} -as long as it does not need to know the type assigned to $C$:\inputencoding{latin9} +External code may use data of type \lstinline!L[A, B]! as long as +it does not need to know the type assigned to $C$: \begin{lstlisting} def toInt: L[Int, Int] => Int = { case L1(a, c, p) => a + p(c) // May apply p: C => B to c: C without knowing what C is. @@ -1563,12 +1363,11 @@ \subsection{Types with existential quantifiers} scala> toInt(q1) res0: Int = 4 \end{lstlisting} -\inputencoding{utf8}In this code, the expression \inputencoding{latin9}\lstinline!p(c)!\inputencoding{utf8} -uses data of unknown type \inputencoding{latin9}\lstinline!C!\inputencoding{utf8} -but does not try to inspect that type. +In this code, the expression \lstinline!p(c)! uses data of unknown +type \lstinline!C! but does not try to inspect that type. To implement the type $\exists C.\,A\times C\times\left(C\rightarrow B\right)$ -via a trait with a type member, we write:\inputencoding{latin9} +via a trait with a type member, we write: \begin{lstlisting} trait L2[A, B] { type C @@ -1587,13 +1386,12 @@ \subsection{Types with existential quantifiers} scala> toInt(q2) res1: Int = 4 \end{lstlisting} -\inputencoding{utf8}The code is more verbose but has equivalent functionality to the code -that uses a case class. Each time we create a value of type \inputencoding{latin9}\lstinline!L[A, B]!\inputencoding{utf8} -or \inputencoding{latin9}\lstinline!L2[A, B]!\inputencoding{utf8}, -we will have to specify some type for \inputencoding{latin9}\lstinline!C!\inputencoding{utf8}. -Code external to the trait \inputencoding{latin9}\lstinline!L2!\inputencoding{utf8} -will no longer know what type was chosen as $C$. It will be a type -error if external code depends on the actual type assigned to \inputencoding{latin9}\lstinline!C!\inputencoding{utf8}:\inputencoding{latin9} +The code is more verbose but has equivalent functionality to the code +that uses a case class. Each time we create a value of type \lstinline!L[A, B]! +or \lstinline!L2[A, B]!, we will have to specify some type for \lstinline!C!. +Code external to the trait \lstinline!L2! will no longer know what +type was chosen as $C$. It will be a type error if external code +depends on the actual type assigned to \lstinline!C!: \begin{lstlisting} scala> val x: q2.C = "abc" // The type q2.C is String but the code may not use that knowledge. ^ @@ -1601,8 +1399,8 @@ \subsection{Types with existential quantifiers} found : String("abc") required: q2.C \end{lstlisting} -\inputencoding{utf8}External code must treat the type \inputencoding{latin9}\lstinline!q2.C!\inputencoding{utf8} -as an unknown, arbitrary type. +External code must treat the type \lstinline!q2.C! as an unknown, +arbitrary type. We will use the existential quantifier notation for proofs of properties of free monads and other constructions. With the new notation for @@ -1615,7 +1413,7 @@ \subsection{Types with existential quantifiers} \end{align*} -\subsection{Expressing types with existential quantifiers via universal quantifiers} +\subsection{Expressing existential quantifiers via universal quantifiers} We will now motivate and define the connection between existential and universal quantifiers. @@ -1630,10 +1428,9 @@ \subsection{Expressing types with existential quantifiers via universal quantifi type assigned to a type parameter. Let us formulate this property in more precise terms. An example of -\textsf{``}external code\textsf{''} is the function \inputencoding{latin9}\lstinline!toInt!\inputencoding{utf8} -shown in the previous section. The type signature and the code of -\inputencoding{latin9}\lstinline!toInt!\inputencoding{utf8} is written -in the short notation as: +\textsf{``}external code\textsf{''} is the function \lstinline!toInt! shown in the +previous section. The type signature and the code of \lstinline!toInt! +is written in the short notation as: \[ \text{toInt}:\left(\exists C.\,\text{Int}\times C\times(C\rightarrow\text{Int})\right)\rightarrow\text{Int}\quad,\quad\quad\text{toInt}\triangleq(\exists C.\,a^{:\text{Int}}\times c^{:C}\times p^{:C\rightarrow\text{Int}})\rightarrow a+p(c)\quad. \] @@ -1664,36 +1461,32 @@ \subsection{Expressing types with existential quantifiers via universal quantifi function argument. To illustrate the type equivalence~(\ref{eq:existential-via-universal}), -we may rewrite the function \inputencoding{latin9}\lstinline!toInt!\inputencoding{utf8} -as:\inputencoding{latin9} +we may rewrite the function \lstinline!toInt! as: \begin{lstlisting} def toIntL1[C]: L1[Int, Int, C] => Int = { case L1(a, c, p) => a + p(c) } \end{lstlisting} -\inputencoding{utf8}The function body of \inputencoding{latin9}\lstinline!toIntL1!\inputencoding{utf8} -is the same as that of \inputencoding{latin9}\lstinline!toInt!\inputencoding{utf8}, +The function body of \lstinline!toIntL1! is the same as that of \lstinline!toInt!, although the type signature changed. To get more intuition for Eq.~(\ref{eq:existential-via-universal}), -first consider a value with a universally quantified type:\inputencoding{latin9} +first consider a value with a universally quantified type: \begin{lstlisting} def f[A]: F[A] = ... \end{lstlisting} -\inputencoding{utf8}The code of the function \inputencoding{latin9}\lstinline!f!\inputencoding{utf8} -can work with any type \inputencoding{latin9}\lstinline!A!\inputencoding{utf8}. +The code of the function \lstinline!f! can work with any type \lstinline!A!. If Scala did not support type parameters, we would need to define -the function \inputencoding{latin9}\lstinline!f!\inputencoding{utf8} -separately for each possible type, as if in a giant infinite tuple: -\inputencoding{latin9}\lstinline!(f[Int], f[String], f[List[Int]], ...)!\inputencoding{utf8}. -In this sense, the definition of \inputencoding{latin9}\lstinline!f[A]!\inputencoding{utf8} -replaces an \textsf{``}infinite product\textsf{''} ranging over all possible types: +the function \lstinline!f! separately for each possible type, as +if in a giant infinite tuple: \lstinline!(f[Int], f[String], f[List[Int]], ...)!. +In this sense, the definition of \lstinline!f[A]! replaces an \textsf{``}infinite +product\textsf{''} ranging over all possible types: \[ f:\forall A.\,F^{A}\quad,\quad\quad\forall A.\,F^{A}=F^{\text{Int}}\times F^{\text{String}}\times F^{\text{List}^{\text{Int}}}\times... \] This formula is not rigorous (the \textsf{``}infinite product\textsf{''} is not clearly defined). Importantly, this formula does not express the requirement -that the code of the function \inputencoding{latin9}\lstinline!f!\inputencoding{utf8} -must work in the same way for all types \inputencoding{latin9}\lstinline!A!\inputencoding{utf8}. -We will use this formula only as a heuristic illustration. +that the code of the function \lstinline!f! must work in the same +way for all types \lstinline!A!. We will use this formula only as +a heuristic illustration. A value of an existentially quantified type, such as $\exists C.\,F^{C}$, must be created by using a specific type for $C$. So, we may view @@ -1720,19 +1513,17 @@ \subsection{Expressing types with existential quantifiers via universal quantifi This is the same as Eq.~(\ref{eq:existential-via-universal}). Another way to see why Eq.~(\ref{eq:existential-via-universal}) -works is to view the case class constructor \inputencoding{latin9}\lstinline!L1[A, B, C]()!\inputencoding{utf8} -as a function from a triple of type \inputencoding{latin9}\lstinline!(A, C, C => B)!\inputencoding{utf8} -to a value of type \inputencoding{latin9}\lstinline!L[A, B]!\inputencoding{utf8}. -For clarity, let us denote that function temporary by \inputencoding{latin9}\lstinline!f!\inputencoding{utf8} -and write:\inputencoding{latin9} +works is to view the case class constructor \lstinline!L1[A, B, C]()! +as a function from a triple of type \lstinline!(A, C, C => B)! to +a value of type \lstinline!L[A, B]!. For clarity, let us denote that +function temporary by \lstinline!f! and write: \begin{lstlisting} def f[A, B, C](a: A, c: C, p: C => B): L[A, B] = L1(a, c, p) \end{lstlisting} -\inputencoding{utf8}This code is just a function with some type parameters; that is, a +This code is just a function with some type parameters; that is, a function that works in the same way for all types. So, the type notation -for the function \inputencoding{latin9}\lstinline!f!\inputencoding{utf8} -must be written via the \emph{universal} quantifiers for each type -parameter: +for the function \lstinline!f! must be written via the \emph{universal} +quantifiers for each type parameter: \[ f:\forall(A,B,C).\,A\times C\times(C\rightarrow B)\rightarrow L^{A,B}\quad. \] @@ -1742,12 +1533,10 @@ \subsection{Expressing types with existential quantifiers via universal quantifi f:\forall(A,B).\,\big(\exists C.\,A\times C\times(C\rightarrow B)\big)\rightarrow L^{A,B}=\forall(A,B).\,\big(\exists C.\,\text{L1}^{A,B,C})\rightarrow L^{A,B}\quad. \] This explains why the existential quantifier is represented in Scala -by a case class with an extra type parameter, such as \inputencoding{latin9}\lstinline!L1[A, B, C]!\inputencoding{utf8}. -The type parameter \inputencoding{latin9}\lstinline!C!\inputencoding{utf8} -is existentially quantified because it does not appear in \inputencoding{latin9}\lstinline!L[A, B]!\inputencoding{utf8}, -while the two other parameters (\inputencoding{latin9}\lstinline!A!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!B!\inputencoding{utf8}) do appear -in \inputencoding{latin9}\lstinline!L[A, B]!\inputencoding{utf8} +by a case class with an extra type parameter, such as \lstinline!L1[A, B, C]!. +The type parameter \lstinline!C! is existentially quantified because +it does not appear in \lstinline!L[A, B]!, while the two other parameters +(\lstinline!A! and \lstinline!B!) do appear in \lstinline!L[A, B]! and are therefore universally quantified. Motivated by these considerations, we may \emph{define} the meaning @@ -1792,21 +1581,19 @@ \section{Free constructions for other typeclasses} The first step is to define the raw tree encoding of the free typeclass. For the free monad, we wrote a case class for each of the monad\textsf{'}s -standard methods (\inputencoding{latin9}\lstinline!map!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8}, \inputencoding{latin9}\lstinline!pure!\inputencoding{utf8}) +standard methods (\lstinline!map!, \lstinline!flatMap!, \lstinline!pure!) and another case class for wrapping the given set of DSL operations. The same recipe gives a raw tree encoding for many other free typeclasses, as long as the typeclass operations are of the form of functions whose last return type is again of the same typeclass. For instance, a monad -\inputencoding{latin9}\lstinline!M!\inputencoding{utf8}\textsf{'}s \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -has the type signature of the form \inputencoding{latin9}\lstinline!... => M[A]!\inputencoding{utf8}, -where the last return type uses the same monad \inputencoding{latin9}\lstinline!M!\inputencoding{utf8}. +\lstinline!M!\textsf{'}s \lstinline!flatMap! has the type signature of the +form \lstinline!... => M[A]!, where the last return type uses the +same monad \lstinline!M!. The second step is to implement the \textsf{``}universal runner\textsf{''} that transforms a free typeclass value into a value of another type belonging to the same typeclass. For instance, the universal runner for the free monad -on \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} takes -a polymorphic function of type $\forall A.\,F^{A}\rightarrow M^{A}$, +on \lstinline!F! takes a polymorphic function of type $\forall A.\,F^{A}\rightarrow M^{A}$, where $M$ is any other monad, and runs the free monad\textsf{'}s effects into $M$\textsf{'}s effects. @@ -1823,67 +1610,62 @@ \subsection{Free pointed types} One of the simplest typeclasses is the \textbf{pointed}\index{pointed type} type, that is, a type that has a designated default value. This is -the \inputencoding{latin9}\lstinline!HasDefault!\inputencoding{utf8} -typeclass from Example~\ref{subsec:tc-Example-Pointed-type}. Let -us now apply the free typeclass construction to this example. We will -obtain a type constructor \inputencoding{latin9}\lstinline!FreeDefault[T]!\inputencoding{utf8} -that wraps an arbitrary type \inputencoding{latin9}\lstinline!T!\inputencoding{utf8} -and has a \inputencoding{latin9}\lstinline!HasDefault!\inputencoding{utf8} +the \lstinline!HasDefault! typeclass from Example~\ref{subsec:tc-Example-Pointed-type}. +Let us now apply the free typeclass construction to this example. +We will obtain a type constructor \lstinline!FreeDefault[T]! that +wraps an arbitrary type \lstinline!T! and has a \lstinline!HasDefault! type instance. The first step is to formulate the required methods of the typeclass -as functions with known type signatures. The \inputencoding{latin9}\lstinline!HasDefault!\inputencoding{utf8} -typeclass for a pointed type \inputencoding{latin9}\lstinline!P!\inputencoding{utf8} -has only one operation: obtaining a value \inputencoding{latin9}\lstinline!default!\inputencoding{utf8} -of type \inputencoding{latin9}\lstinline!P!\inputencoding{utf8}. -This operation is equivalent to a function of type $\bbnum 1\rightarrow P$. +as functions with known type signatures. The \lstinline!HasDefault! +typeclass for a pointed type \lstinline!P! has only one operation: +obtaining a value \lstinline!default! of type \lstinline!P!. This +operation is equivalent to a function of type $\bbnum 1\rightarrow P$. The free typeclass will model this operation via a case class containing a value of unit type (equivalently, a named unit). We may write the -code like this:\inputencoding{latin9} +code like this: \begin{lstlisting} sealed trait FreeDefault[T] final case class Default[T](unit: Unit) extends FreeDefault[T] final case class Op[T](op: T) extends FreeDefault[T] \end{lstlisting} -\inputencoding{utf8}In the type notation, this is written as: +In the type notation, this is written as: \[ \text{FreeDefault}^{T}\triangleq\bbnum 1+T\quad. \] - We find that \inputencoding{latin9}\lstinline!FreeDefault[T]!\inputencoding{utf8} -is equivalent to \inputencoding{latin9}\lstinline!Option[T]!\inputencoding{utf8}, -so we will define it that way:\inputencoding{latin9} + We find that \lstinline!FreeDefault[T]! is equivalent to \lstinline!Option[T]!, +so we will define it that way: \begin{lstlisting} type FreeDefault[T] = Option[T] \end{lstlisting} -\inputencoding{utf8} + \subsubsection{Definition \label{subsec:Definition-free-pointed-type}\ref{subsec:Definition-free-pointed-type}} The \textbf{free pointed type} on\index{free pointed type} a given type $T$ is the type $\bbnum 1+T$. -The \inputencoding{latin9}\lstinline!HasDefault!\inputencoding{utf8} -typeclass has no laws, so the raw tree encoding ($\bbnum 1+T$) cannot -be reduced by imposing any typeclass laws. +The \lstinline!HasDefault! typeclass has no laws, so the raw tree +encoding ($\bbnum 1+T$) cannot be reduced by imposing any typeclass +laws. The free pointed typeclass comes with a \textsf{``}universal runner\textsf{''}, which -is a function from \inputencoding{latin9}\lstinline!FreeDefault[T]!\inputencoding{utf8} -to a chosen pointed type $P$. The runner needs to know how to translate -$T$ into $P$, and the code can be written as:\inputencoding{latin9} +is a function from \lstinline!FreeDefault[T]! to a chosen pointed +type $P$. The runner needs to know how to translate $T$ into $P$, +and the code can be written as: \begin{lstlisting} def runner[T, P: HasDefault](run: T => P): Option[T] => P = { case Some(t) => run(t) case None => implicitly[HasDefault[P]].value // The default value of type P. } \end{lstlisting} -\inputencoding{utf8}This logic repeats the Scala standard library\textsf{'}s \inputencoding{latin9}\lstinline!getOrElse!\inputencoding{utf8} -method for \inputencoding{latin9}\lstinline!Option!\inputencoding{utf8}. -For any value \inputencoding{latin9}\lstinline!x: Option[T]!\inputencoding{utf8}:\inputencoding{latin9} +This logic repeats the Scala standard library\textsf{'}s \lstinline!getOrElse! +method for \lstinline!Option!. For any value \lstinline!x: Option[T]!: \begin{lstlisting} runner(run)(x) == x.map(run).getOrElse(implicitly[HasDefault[P]].value) \end{lstlisting} -\inputencoding{utf8}So, we can view \inputencoding{latin9}\lstinline!getOrElse!\inputencoding{utf8} -as a general form of the universal runner for the free pointed typeclass. +So, we can view \lstinline!getOrElse! as a general form of the universal +runner for the free pointed typeclass. What if the type $T$ is already pointed? The free pointed type on $T$ is $\bbnum 1+T$, which is never equivalent to $T$. However, @@ -1891,93 +1673,83 @@ \subsubsection{Definition \label{subsec:Definition-free-pointed-type}\ref{subsec $T\rightarrow T$ and gives a function of type $\bbnum 1+T\rightarrow T$. When $T$ is itself of the form $\bbnum 1+U$ then that function (of type $\bbnum 1+\bbnum 1+U\rightarrow\bbnum 1+U$) is the standard -method \inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} -defined on \inputencoding{latin9}\lstinline!Option!\inputencoding{utf8} -types. +method \lstinline!flatten! defined on \lstinline!Option! types. -In this way, \inputencoding{latin9}\lstinline!Option!\inputencoding{utf8}\textsf{'}s -standard methods \inputencoding{latin9}\lstinline!getOrElse!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} -are seen as regular properties of the free pointed type construction. +In this way, \lstinline!Option!\textsf{'}s standard methods \lstinline!getOrElse! +and \lstinline!flatten! are seen as regular properties of the free +pointed type construction. \subsection{Free semigroups} A semigroup (see Example~\ref{subsec:tc-Example-Semigroups}) is -a typeclass with a single method called \inputencoding{latin9}\lstinline!combine!\inputencoding{utf8}. -A common notation for \inputencoding{latin9}\lstinline!combine!\inputencoding{utf8} -is $\oplus$, used as an infix binary operation with type signature -$\oplus:T\times T\rightarrow T$. The semigroup\textsf{'}s law is the associativity -law for the operation $\oplus$. +a typeclass with a single method called \lstinline!combine!. A common +notation for \lstinline!combine! is $\oplus$, used as an infix binary +operation with type signature $\oplus:T\times T\rightarrow T$. The +semigroup\textsf{'}s law is the associativity law for the operation $\oplus$. How can we convert an arbitrary type $T$ into a semigroup? Following the general recipe, we first construct the raw tree encoding of a free semigroup on a given type $T$. There is one case class for the -binary operation and another for wrapping a value of type $T$.\inputencoding{latin9} +binary operation and another for wrapping a value of type $T$. \begin{lstlisting} sealed trait FSR[T] final case class Combine[T](left: FSR[T], right: FSR[T]) extends FSR[T] final case class Wrap[T](value: T) extends FSR[T] \end{lstlisting} -\inputencoding{utf8}The short notation for this type constructor is: +The short notation for this type constructor is: \[ \text{FSR}^{T}\triangleq T+\text{FSR}^{T}\times\text{FSR}^{T}\quad. \] -We can now see that \inputencoding{latin9}\lstinline!FSR[T]!\inputencoding{utf8} -is a binary tree with leaf values of type $T$ (see Section~\subsecref{Binary-trees}). +We can now see that \lstinline!FSR[T]! is a binary tree with leaf +values of type $T$ (see Section~\subsecref{Binary-trees}). -We can implement a \inputencoding{latin9}\lstinline!Semigroup!\inputencoding{utf8} -typeclass instance for \inputencoding{latin9}\lstinline!FSR[T]!\inputencoding{utf8} -like this:\inputencoding{latin9} +We can implement a \lstinline!Semigroup! typeclass instance for \lstinline!FSR[T]! +like this: \begin{lstlisting} implicit def semigroupFSR[T]: Semigroup[FSR[T]] = Semigroup((l, r) => Combine(l, r)) *** verify code \end{lstlisting} -\inputencoding{utf8} + For convenience, we will define a syntax extension so that we can -use the infix binary operation \inputencoding{latin9}\lstinline!|+|!\inputencoding{utf8}:\inputencoding{latin9} +use the infix binary operation \lstinline!|+|!: \begin{lstlisting} implicit class SemigroupOp[S: Semigroup](s: S) { def |+|(other: S): S = implicitly[Semigroup[S]].combine(s, other) }*** check if it works \end{lstlisting} -\inputencoding{utf8} + As usual with raw tree encodings, the free semigroup\textsf{'}s binary operation -\inputencoding{latin9}\lstinline!|+|!\inputencoding{utf8} does not -perform any computations but simply wraps its arguments into the case -class \inputencoding{latin9}\lstinline!Combine!\inputencoding{utf8}. -A \textsf{``}free semigroup program\textsf{''} (specifically, an \inputencoding{latin9}\lstinline!FSR!\inputencoding{utf8}-program) -is an unevaluated expression tree containing a number of nested \inputencoding{latin9}\lstinline!Combine!\inputencoding{utf8} -case classes as well as some values of type \inputencoding{latin9}\lstinline!T!\inputencoding{utf8} -wrapped in \inputencoding{latin9}\lstinline!Wrap!\inputencoding{utf8}. +\lstinline!|+|! does not perform any computations but simply wraps +its arguments into the case class \lstinline!Combine!. A \textsf{``}free +semigroup program\textsf{''} (specifically, an \lstinline!FSR!-program) is +an unevaluated expression tree containing a number of nested \lstinline!Combine! +case classes as well as some values of type \lstinline!T! wrapped +in \lstinline!Wrap!. The next step is to implement a universal runner that will evaluate -that expression tree. Given any semigroup \inputencoding{latin9}\lstinline!S!\inputencoding{utf8} -and a function \inputencoding{latin9}\lstinline!T => S!\inputencoding{utf8}, -we can run \inputencoding{latin9}\lstinline!FSR[T]!\inputencoding{utf8} -into \inputencoding{latin9}\lstinline!S!\inputencoding{utf8} like -this:\inputencoding{latin9} +that expression tree. Given any semigroup \lstinline!S! and a function +\lstinline!T => S!, we can run \lstinline!FSR[T]! into \lstinline!S! +like this: \begin{lstlisting} def runner[S: Semigroup, T](runT: T => S): FSR[T] => S = { case Combine(left, right) => runner(runT)(left) |+| runner(runT)(right) case Wrap(value) => runT(value) } \end{lstlisting} -\inputencoding{utf8} -This code is analogous to the code of \inputencoding{latin9}\lstinline!foldMap!\inputencoding{utf8} -\index{foldMap function@\texttt{foldMap} function}for aggregating -tree-like data (Section~\subsecref{Aggregating-tree-like-data-bfs}). -Let us see an example of an \inputencoding{latin9}\lstinline!FSR!\inputencoding{utf8}-program -for a free semigroup on \inputencoding{latin9}\lstinline!String!\inputencoding{utf8}:\inputencoding{latin9} +This code is analogous to the code of \lstinline!foldMap! \index{foldMap function@\texttt{foldMap} function}for +aggregating tree-like data (Section~\subsecref{Aggregating-tree-like-data-bfs}). + +Let us see an example of an \lstinline!FSR!-program for a free semigroup +on \lstinline!String!: \begin{lstlisting} val fsfProgram: FSR[String] = Wrap("abc") |+| (Wrap("xyz") |+| Wrap("")) \end{lstlisting} -\inputencoding{utf8} To run this program, we need to choose another semigroup (\inputencoding{latin9}\lstinline!S!\inputencoding{utf8}) -and provide a function that maps \inputencoding{latin9}\lstinline!String!\inputencoding{utf8} -to \inputencoding{latin9}\lstinline!S!\inputencoding{utf8}. We choose -\inputencoding{latin9}\lstinline!S = Int!\inputencoding{utf8}, a -semigroup with respect to integer addition. The function \inputencoding{latin9}\lstinline!runT: String => Int!\inputencoding{utf8} -will return the length of the string. In this way, we can compute -the total length of all strings within the \inputencoding{latin9}\lstinline!FSR!\inputencoding{utf8}-program:\inputencoding{latin9} + To run this program, we need to choose another semigroup (\lstinline!S!) +and provide a function that maps \lstinline!String! to \lstinline!S!. +We choose \lstinline!S = Int!, a semigroup with respect to integer +addition. The function \lstinline!runT: String => Int! will return +the length of the string. In this way, we can compute the total length +of all strings within the \lstinline!FSR!-program: \begin{lstlisting} *** test that this works implicit semigroupInt: Semigroup[Int] = Semigroup(_ + _) @@ -1985,45 +1757,39 @@ \subsection{Free semigroups} scala> runner(_.length)(fsfProgram) res0: Int = 6 \end{lstlisting} -\inputencoding{utf8} -The associativity law of \inputencoding{latin9}\lstinline!|+|!\inputencoding{utf8} -will hold after applying the runner to an \inputencoding{latin9}\lstinline!FSR!\inputencoding{utf8}-program -because associativity is assumed to hold for the operation \inputencoding{latin9}\lstinline!|+|!\inputencoding{utf8} -of the semigroup \inputencoding{latin9}\lstinline!S!\inputencoding{utf8}. -However, the raw tree encoding itself does not obey the associativity -law because the nested data structure \inputencoding{latin9}\lstinline!Combine(Combine(x, y), z)!\inputencoding{utf8} -is not equal to \inputencoding{latin9}\lstinline!Combine(x, Combine(y, z))!\inputencoding{utf8}. -The next step is to look for a reduced encoding of the free semigroup -that obeys the associativity law. - -The associativity law would hold if the result of \inputencoding{latin9}\lstinline!Combine(x, y) |+| z!\inputencoding{utf8} -were not \inputencoding{latin9}\lstinline!Combine(Combine(x, y), z)!\inputencoding{utf8} -but \inputencoding{latin9}\lstinline!Combine(x, Combine(y, z))!\inputencoding{utf8}. -To achieve this, we could redefine the \inputencoding{latin9}\lstinline!|+|!\inputencoding{utf8} -operation by modifying the \inputencoding{latin9}\lstinline!Semigroup!\inputencoding{utf8} -typeclass instance:\inputencoding{latin9} + +The associativity law of \lstinline!|+|! will hold after applying +the runner to an \lstinline!FSR!-program because associativity is +assumed to hold for the operation \lstinline!|+|! of the semigroup +\lstinline!S!. However, the raw tree encoding itself does not obey +the associativity law because the nested data structure \lstinline!Combine(Combine(x, y), z)! +is not equal to \lstinline!Combine(x, Combine(y, z))!. The next step +is to look for a reduced encoding of the free semigroup that obeys +the associativity law. + +The associativity law would hold if the result of \lstinline!Combine(x, y) |+| z! +were not \lstinline!Combine(Combine(x, y), z)! but \lstinline!Combine(x, Combine(y, z))!. +To achieve this, we could redefine the \lstinline!|+|! operation +by modifying the \lstinline!Semigroup! typeclass instance: \begin{lstlisting} implicit def semigroupFSR[T]: Semigroup[FSR[T]] = Semigroup((l, r) => l match { case Wrap(value) => Combine(l, r) case Combine(p, q) => p |+| (q |+| r) // Recursive call of |+|. }) *** verify that this code works \end{lstlisting} -\inputencoding{utf8}The data structure from the new \inputencoding{latin9}\lstinline!|+|!\inputencoding{utf8} -operation will be of the form \inputencoding{latin9}\lstinline!Combine(Wrap(x), Combine(Wrap(y), ...))!\inputencoding{utf8}, +The data structure from the new \lstinline!|+|! operation will be +of the form \lstinline!Combine(Wrap(x), Combine(Wrap(y), ...))!, and the associativity law will always hold. The new data structure is equivalent to a non-empty list containing -values of type \inputencoding{latin9}\lstinline!Wrap[T]!\inputencoding{utf8}. -The resulting type (\inputencoding{latin9}\lstinline!NEL[Wrap[T]]!\inputencoding{utf8}) -can be simplified to just \inputencoding{latin9}\lstinline!NEL[T]!\inputencoding{utf8} -since a nested \inputencoding{latin9}\lstinline!Wrap!\inputencoding{utf8} +values of type \lstinline!Wrap[T]!. The resulting type (\lstinline!NEL[Wrap[T]]!) +can be simplified to just \lstinline!NEL[T]! since a nested \lstinline!Wrap! carries no functionality by itself. So, we can reuse the non-empty -list type \inputencoding{latin9}\lstinline!NEL!\inputencoding{utf8} -and its \inputencoding{latin9}\lstinline!concat!\inputencoding{utf8} -function (see Example~\ref{subsec:Disjunctive-Example-non-empty-list-foldLeft} +list type \lstinline!NEL! and its \lstinline!concat! function (see +Example~\ref{subsec:Disjunctive-Example-non-empty-list-foldLeft} and Exercise~\ref{subsec:Disjunctive-Exercise-non-empty-list-2}). -The universal runner is similar to the code of the \inputencoding{latin9}\lstinline!foldLeft!\inputencoding{utf8} -function shown in Example~\ref{subsec:Disjunctive-Example-non-empty-list-foldLeft}:\inputencoding{latin9} +The universal runner is similar to the code of the \lstinline!foldLeft! +function shown in Example~\ref{subsec:Disjunctive-Example-non-empty-list-foldLeft}: \begin{lstlisting} implicit def semigroupNEL[T]: Semigroup[NEL[T]] = Semigroup((l, r) => concat(l, r)) @@ -2032,82 +1798,75 @@ \subsection{Free semigroups} case More(x, tail) => runner(runT)(tail) } \end{lstlisting} -\inputencoding{utf8} -This defines \inputencoding{latin9}\lstinline!NEL[T]!\inputencoding{utf8} -as a reduced encoding of the free semigroup on \inputencoding{latin9}\lstinline!T!\inputencoding{utf8}. -Comparing the raw tree encoding \inputencoding{latin9}\lstinline!FSR[T]!\inputencoding{utf8} -and the reduced encoding \inputencoding{latin9}\lstinline!NEL[T]!\inputencoding{utf8}, -we find some differences in run-time performance. For \inputencoding{latin9}\lstinline!NEL[T]!\inputencoding{utf8}, -the binary operation \inputencoding{latin9}\lstinline!|+|!\inputencoding{utf8} +This defines \lstinline!NEL[T]! as a reduced encoding of the free +semigroup on \lstinline!T!. + +Comparing the raw tree encoding \lstinline!FSR[T]! and the reduced +encoding \lstinline!NEL[T]!, we find some differences in run-time +performance. For \lstinline!NEL[T]!, the binary operation \lstinline!|+|! involves concatenating two lists, which may require traversing the lists. The raw tree encoding\textsf{'}s binary operation takes constant time -as it only wraps the data in a new case class. However, \inputencoding{latin9}\lstinline!NEL[T]!\inputencoding{utf8}\textsf{'}s -run operation may be implemented with tail recursion while \inputencoding{latin9}\lstinline!FSR[T]!\inputencoding{utf8} +as it only wraps the data in a new case class. However, \lstinline!NEL[T]!\textsf{'}s +run operation may be implemented with tail recursion while \lstinline!FSR[T]! is a binary tree whose traversal cannot be tail-recursive. \subsection{Free monoid and its partially lawful encodings\label{subsec:Free-monoids}} A monoid (see Example~\ref{subsec:tc-Example-Monoids}) is a typeclass -with a two methods: \inputencoding{latin9}\lstinline!combine!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!empty!\inputencoding{utf8}. -The monoid\textsf{'}s laws are the associativity law and two identity laws. +with a two methods: \lstinline!combine! and \lstinline!empty!. The +monoid\textsf{'}s laws are the associativity law and two identity laws. To convert an arbitrary type $T$ into a free monoid on $T$, we follow the general recipe and write the raw tree encoding. There is one case -class for the binary operation, one case class for \inputencoding{latin9}\lstinline!empty!\inputencoding{utf8}, -and one for wrapping a value of type $T$.\inputencoding{latin9} +class for the binary operation, one case class for \lstinline!empty!, +and one for wrapping a value of type $T$. \begin{lstlisting} sealed trait FMR[T] final case class Combine[T](left: FMR[T], right: FMR[T]) extends FMR[T] final case class Empty[T]() extends FMR[T] final case class Wrap[T](value: T) extends FMR[T] \end{lstlisting} -\inputencoding{utf8}The short notation for this type constructor is: +The short notation for this type constructor is: \[ \text{FMR}^{T}\triangleq\bbnum 1+T+\text{FMR}^{T}\times\text{FMR}^{T}\quad. \] -We can see that \inputencoding{latin9}\lstinline!FMR[T]!\inputencoding{utf8} -is just a binary tree with leaf values of type $\bbnum 1+T$: +We can see that \lstinline!FMR[T]! is just a binary tree with leaf +values of type $\bbnum 1+T$: \[ \text{FMR}^{T}=\text{Tree2}^{\bbnum 1+T}\quad. \] -The binary tree type \inputencoding{latin9}\lstinline!Tree2!\inputencoding{utf8} -was defined in Section~\ref{subsec:Binary-trees}. The data type -$\text{FMR}^{T}$ represents an \emph{unevaluated} expression tree -built from the monoid operations and from values of type $T$, for -example: +The binary tree type \lstinline!Tree2! was defined in Section~\ref{subsec:Binary-trees}. +The data type $\text{FMR}^{T}$ represents an \emph{unevaluated} expression +tree built from the monoid operations and from values of type $T$, +for example: \begin{wrapfigure}{l}{0.6\columnwidth}% \vspace{-0.4\baselineskip} -\inputencoding{latin9}\begin{lstlisting} +\begin{lstlisting} val exampleFMR: FMR[Int] = Combine(Empty(), Combine( Combine(Wrap(456), Empty()), Wrap(123))) \end{lstlisting} -\inputencoding{utf8}\vspace{0.2\baselineskip} +\vspace{0.2\baselineskip} \end{wrapfigure}% \noindent {\tiny{}\hspace{0.1\columnwidth}}{\tiny{} \Tree[ [ $e$ ] [ [ [ $456$ ] [ $e$ ] ] [ $123$ ] ] ] }{\tiny\par} -We can implement a \inputencoding{latin9}\lstinline!Monoid!\inputencoding{utf8} -typeclass instance for \inputencoding{latin9}\lstinline!FMR[T]!\inputencoding{utf8} -like this:\inputencoding{latin9} +We can implement a \lstinline!Monoid! typeclass instance for \lstinline!FMR[T]! +like this: \begin{lstlisting} implicit def monoidFMR[T]: Monoid[FMR[T]] = Monoid((l, r) => Combine(l, r), Empty()) \end{lstlisting} -\inputencoding{utf8}We define a syntax extension for the infix binary operation \inputencoding{latin9}\lstinline!|+|!\inputencoding{utf8} +We define a syntax extension for the infix binary operation \lstinline!|+|! as in the previous section. As usual with raw tree encodings, this free monoid\textsf{'}s operations not perform any computations but only create nested case classes. Those nested case classes represent an unevaluated expression tree of an -\textsf{``}\inputencoding{latin9}\lstinline!FMR!\inputencoding{utf8}-program\textsf{''}. -The next step is to implement a universal runner that will evaluate -that expression tree using the operations of any chosen monoid \inputencoding{latin9}\lstinline!M!\inputencoding{utf8}. -Given a function \inputencoding{latin9}\lstinline!T => M!\inputencoding{utf8}, -we can convert any \inputencoding{latin9}\lstinline!FMR!\inputencoding{utf8}-program -(a value of type \inputencoding{latin9}\lstinline!FMR[T]!\inputencoding{utf8}) -into a value of type \inputencoding{latin9}\lstinline!M!\inputencoding{utf8} -like this:\inputencoding{latin9} +\textsf{``}\lstinline!FMR!-program\textsf{''}. The next step is to implement a universal +runner that will evaluate that expression tree using the operations +of any chosen monoid \lstinline!M!. Given a function \lstinline!T => M!, +we can convert any \lstinline!FMR!-program (a value of type \lstinline!FMR[T]!) +into a value of type \lstinline!M! like this: \begin{lstlisting} def runnerFMR[M: Monoid, T](runT: T => M)(fmr: FMR[T]): M = fmr match { case Combine(left, right) => runnerFMR(runT)(left) |+| runnerFMR(runT)(right) @@ -2115,64 +1874,58 @@ \subsection{Free monoid and its partially lawful encodings\label{subsec:Free-mon case Wrap(value) => runT(value) } \end{lstlisting} -\inputencoding{utf8} -The raw tree encoding \inputencoding{latin9}\lstinline!FMR[T]!\inputencoding{utf8} -does not satisfy any of the monoid laws (the associativity law and -the two identity laws). This is not a problem in practice, since the -laws will hold after running an \inputencoding{latin9}\lstinline!FMR!\inputencoding{utf8}-program -into any lawful monoid \inputencoding{latin9}\lstinline!M!\inputencoding{utf8}. + +The raw tree encoding \lstinline!FMR[T]! does not satisfy any of +the monoid laws (the associativity law and the two identity laws). +This is not a problem in practice, since the laws will hold after +running an \lstinline!FMR!-program into any lawful monoid \lstinline!M!. Imposing the monoid laws will help us find reduced encodings of the free monoid. As with the free semigroup, the associativity law will hold if we replace a binary tree by a non-empty list. The result is a non-empty -list of values of type $\bbnum 1+T$. The type \inputencoding{latin9}\lstinline!NEL[Option[T]]!\inputencoding{utf8} +list of values of type $\bbnum 1+T$. The type \lstinline!NEL[Option[T]]! is a possible encoding of the free monoid; it satisfies the associativity law but fails the identity laws. The empty value of the free monoid -is represented by a single-element list containing \inputencoding{latin9}\lstinline!None!\inputencoding{utf8} +is represented by a single-element list containing \lstinline!None! (in the code notation, $[1+\bbnum 0^{:T}]$). The identity laws of the monoid would hold if lists of the form: \[ \left[\bbnum 0+x_{1},...,\bbnum 0+x_{m},1+\bbnum 0,\bbnum 0+y_{1},...,\bbnum 0+y_{n}\right] \] were replaced by lists $\left[x_{1},...,x_{m},y_{1},...,y_{n}\right]$ -that do not contain any \inputencoding{latin9}\lstinline!None!\inputencoding{utf8} -values. This would mean that it is enough to use \inputencoding{latin9}\lstinline!NEL[T]!\inputencoding{utf8} -instead of \inputencoding{latin9}\lstinline!NEL[Option[T]]!\inputencoding{utf8}, -except for the possibility that a non-empty list contains only \inputencoding{latin9}\lstinline!None!\inputencoding{utf8} +that do not contain any \lstinline!None! values. This would mean +that it is enough to use \lstinline!NEL[T]! instead of \lstinline!NEL[Option[T]]!, +except for the possibility that a non-empty list contains only \lstinline!None! values. For simplicity, we can represent that situation by an \emph{empty} -list. So, we may replace \inputencoding{latin9}\lstinline!NEL[Option[T]]!\inputencoding{utf8} -by simply \inputencoding{latin9}\lstinline!List[T]!\inputencoding{utf8}. -The binary operation for \inputencoding{latin9}\lstinline!List[T]!\inputencoding{utf8} -is just the \inputencoding{latin9}\lstinline!concat!\inputencoding{utf8} +list. So, we may replace \lstinline!NEL[Option[T]]! by simply \lstinline!List[T]!. +The binary operation for \lstinline!List[T]! is just the \lstinline!concat! function for lists; the empty list is the empty monoid value. -The type \inputencoding{latin9}\lstinline!List[T]!\inputencoding{utf8} -is the shortest reduced encoding for the free monoid on $T$, and -it satisfies all monoid laws. The runner is the same as the \inputencoding{latin9}\lstinline!foldMap!\inputencoding{utf8} -function for lists (see Section~\ref{subsec:From-reduce-and-foldleft-to-foldmap}):\index{foldMap function@\texttt{foldMap} function}\inputencoding{latin9} +The type \lstinline!List[T]! is the shortest reduced encoding for +the free monoid on $T$, and it satisfies all monoid laws. The runner +is the same as the \lstinline!foldMap! function for lists (see Section~\ref{subsec:From-reduce-and-foldleft-to-foldmap}):\index{foldMap function@\texttt{foldMap} function} \begin{lstlisting} def runnerList[M: Monoid, T](runT: T => M): List[T] => M = foldMap[M, T](runT) \end{lstlisting} -\inputencoding{utf8} + There exist other reduced encodings that satisfy only a subset of the monoid laws. As an example, let us define a reduced encoding of the free monoid that satisfies the identity laws but not the associativity law. To impose the identity laws on $\text{Tree2}^{\bbnum 1+T}$, we note that the identity laws reduce expression trees creating \textsf{``}empty\textsf{''} leaf values ($1+\bbnum 0^{:T}$) to expression trees that do not contain -any \textsf{``}empty\textsf{''} values in the leaves. For instance, the value \inputencoding{latin9}\lstinline!exampleFMR!\inputencoding{utf8} -defined above will be reduced to just \inputencoding{latin9}\lstinline!Combine(Wrap(456), Wrap(123))!\inputencoding{utf8}. -If all leaves of the expression tree are \inputencoding{latin9}\lstinline!Empty!\inputencoding{utf8}, -the tree must be reduced to just a single empty value. This means -we can simplify the encoding from $\text{Tree2}^{\bbnum 1+T}$ to -$\bbnum 1+\text{Tree2}^{T}$. +any \textsf{``}empty\textsf{''} values in the leaves. For instance, the value \lstinline!exampleFMR! +defined above will be reduced to just \lstinline!Combine(Wrap(456), Wrap(123))!. +If all leaves of the expression tree are \lstinline!Empty!, the tree +must be reduced to just a single empty value. This means we can simplify +the encoding from $\text{Tree2}^{\bbnum 1+T}$ to $\bbnum 1+\text{Tree2}^{T}$. To summarize, we have obtained four different \textsf{``}partially lawful\textsf{''} free monoid encodings:\\ \textbf{1)} The encoding $F_{1}^{T}\triangleq\text{Tree2}^{\bbnum 1+T}$ -(called \inputencoding{latin9}\lstinline!FMR[T]!\inputencoding{utf8} -above) satisfies none of the monoid laws. The code is:\inputencoding{latin9} +(called \lstinline!FMR[T]! above) satisfies none of the monoid laws. +The code is: \begin{lstlisting} type F1[T] = Tree2[Option[T]] def wrapF1[T](t: T): F1[T] = Leaf(Some(t)) @@ -2183,17 +1936,17 @@ \subsection{Free monoid and its partially lawful encodings\label{subsec:Free-mon case Leaf(Some(value)) => runT(value) } \end{lstlisting} -\inputencoding{utf8} + \textbf{2)} The encoding $F_{2}^{T}\triangleq\text{NEL}^{\bbnum 1+T}$ -satisfies only the associativity law. The code is:\inputencoding{latin9} +satisfies only the associativity law. The code is: \begin{lstlisting} type F2[T] = NEL[Option[T]] def wrapF2[T](t: T): F2[T] = (Some(t), Nil) implicit def monoidF2[T]: Monoid[F2[T]] = Monoid(NEL.concat, (None, Nil)) def runnerF2[M: Monoid, T](runT: T => M)(fmr: F2[T]): M = foldMap(runT).apply(fmr.toList.flatten) \end{lstlisting} -\inputencoding{utf8}For completeness, here is a simple implementation of non-empty lists -and some methods on them:\inputencoding{latin9} +For completeness, here is a simple implementation of non-empty lists +and some methods on them: \begin{lstlisting} type NEL[T] = (T, List[T]) object NEL { @@ -2205,9 +1958,9 @@ \subsection{Free monoid and its partially lawful encodings\label{subsec:Free-mon } } \end{lstlisting} -\inputencoding{utf8} + \textbf{3)} The encoding $F_{3}^{T}\triangleq\bbnum 1+\text{Tree2}^{T}$ -satisfies only the identity laws. The code is:\inputencoding{latin9} +satisfies only the identity laws. The code is: \begin{lstlisting} type F3[T] = Option[Tree2[T]] def wrapF3[T](t: T): F3[T] = Some(Leaf(t)) @@ -2227,84 +1980,76 @@ \subsection{Free monoid and its partially lawful encodings\label{subsec:Free-mon case None => Monoid[M].empty } \end{lstlisting} -\inputencoding{utf8} + \textbf{4)} The encoding $F_{4}^{T}\triangleq\text{List}^{T}$ satisfies -all the monoid laws. The code is:\inputencoding{latin9} +all the monoid laws. The code is: \begin{lstlisting} type F4[T] = List[T] def wrapF4[T](t: T): F4[T] = List(t) implicit def monoidF4[T]: Monoid[F4[T]] = Monoid(_ ++ _, Nil) def runnerF4[M: Monoid, T](runT: T => M)(fmr: F4[T]): M = foldMap(runT).apply(fmr) \end{lstlisting} -\inputencoding{utf8} + To get more intuition about using these encodings, let us explore -whether the types \inputencoding{latin9}\lstinline!F1!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!F2!\inputencoding{utf8}, \inputencoding{latin9}\lstinline!F3!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!F4!\inputencoding{utf8} can be mapped -into each other. All of those encodings have a \inputencoding{latin9}\lstinline!Monoid!\inputencoding{utf8} -instance, so we can use their runner functions to map any encoding -into any other:\inputencoding{latin9} +whether the types \lstinline!F1!, \lstinline!F2!, \lstinline!F3!, +\lstinline!F4! can be mapped into each other. All of those encodings +have a \lstinline!Monoid! instance, so we can use their runner functions +to map any encoding into any other: \begin{lstlisting} def f1_to_f2[T]: F1[T] => F2[T] = runnerF1(wrapF2[T](_)) def f3_to_f2[T]: F3[T] => F2[T] = runnerF3(wrapF2[T](_)) def f4_to_f3[T]: F4[T] => F3[T] = runnerF4(wrapF3[T](_)) // And so on. \end{lstlisting} -\inputencoding{utf8}But it turns out that some of those mappings fail to preserve the +But it turns out that some of those mappings fail to preserve the monoid operations. For instance, converting a list $\left[1,2,3\right]$ -of type \inputencoding{latin9}\lstinline!F4[Int]!\inputencoding{utf8} -to the type \inputencoding{latin9}\lstinline!F3[Int]!\inputencoding{utf8} -will create the following tree:\inputencoding{latin9} +of type \lstinline!F4[Int]! to the type \lstinline!F3[Int]! will +create the following tree: \begin{lstlisting} scala> f4_to_f3(List(1, 2, 3)) res0: F3[Int] = Some(Branch(Branch(Leaf(1), Leaf(2)), Leaf(3))) \end{lstlisting} -\inputencoding{utf8}The same value \inputencoding{latin9}\lstinline!List(1, 2, 3)!\inputencoding{utf8} -can be computed via \inputencoding{latin9}\lstinline!F4!\inputencoding{utf8}\textsf{'}s -monoid operation as \inputencoding{latin9}\lstinline!List(1) |+| List(2, 3)!\inputencoding{utf8}. -However, converting the two shorter lists to \inputencoding{latin9}\lstinline!F3[Int]!\inputencoding{utf8} -and combining them via \inputencoding{latin9}\lstinline!F3!\inputencoding{utf8}\textsf{'}s -monoid operation will give a different tree:\inputencoding{latin9} +The same value \lstinline!List(1, 2, 3)! can be computed via \lstinline!F4!\textsf{'}s +monoid operation as \lstinline!List(1) |+| List(2, 3)!. However, +converting the two shorter lists to \lstinline!F3[Int]! and combining +them via \lstinline!F3!\textsf{'}s monoid operation will give a different +tree: \begin{lstlisting} scala> f4_to_f3(List(1)) |+| f4_to_f3(List(2, 3)) res1: F3[Int] = Some(Branch(Leaf(1), Branch(Leaf(2), Leaf(3)))) \end{lstlisting} -\inputencoding{utf8}The trees are not the same because \inputencoding{latin9}\lstinline!F3!\inputencoding{utf8} -does not obey the monoid associativity law while \inputencoding{latin9}\lstinline!F4!\inputencoding{utf8} -does. +The trees are not the same because \lstinline!F3! does not obey the +monoid associativity law while \lstinline!F4! does. {*}{*}{*}Show injectivity of those transformations Looking at these examples, we find that whenever a free monoid encoding -\inputencoding{latin9}\lstinline!P!\inputencoding{utf8} obeys \emph{more -laws} than another encoding \inputencoding{latin9}\lstinline!Q!\inputencoding{utf8}, -the transformation of type \inputencoding{latin9}\lstinline!P => Q!\inputencoding{utf8} -does not preserve the monoid operations (while the opposite one, \inputencoding{latin9}\lstinline!Q => P!\inputencoding{utf8}, -does). At the same time, the transformation of type \inputencoding{latin9}\lstinline!P => Q!\inputencoding{utf8} -is injective, suggesting that the encoding is \textsf{``}smaller\textsf{''} when it -satisfies more laws. These observations will be generalized in Section~\ref{subsec:Free--typeclasses-that-satisfy-laws} +\lstinline!P! obeys \emph{more laws} than another encoding \lstinline!Q!, +the transformation of type \lstinline!P => Q! does not preserve the +monoid operations (while the opposite one, \lstinline!Q => P!, does). +At the same time, the transformation of type \lstinline!P => Q! is +injective, suggesting that the encoding is \textsf{``}smaller\textsf{''} when it satisfies +more laws. These observations will be generalized in Section~\ref{subsec:Free--typeclasses-that-satisfy-laws} to a rigorous theory of free typeclass encodings that satisfy only a subset of the laws of a given typeclass. \subsection{Free functors} -Consider the \inputencoding{latin9}\lstinline!Functor!\inputencoding{utf8} -typeclass whose only method is \inputencoding{latin9}\lstinline!fmap!\inputencoding{utf8}: +Consider the \lstinline!Functor! typeclass whose only method is \lstinline!fmap!: \[ \text{fmap}:\left(A\rightarrow B\right)\rightarrow F^{A}\rightarrow F^{B}\quad. \] -For some type constructors $F$, the \inputencoding{latin9}\lstinline!fmap!\inputencoding{utf8} -method is not available. An example is when $F$ is a contrafunctor -or an unfunctor. (See Section~\ref{subsec:Examples-of-non-functors} -for more examples.) Let us now apply the raw tree encoding recipe -to the \inputencoding{latin9}\lstinline!Functor!\inputencoding{utf8} +For some type constructors $F$, the \lstinline!fmap! method is not +available. An example is when $F$ is a contrafunctor or an unfunctor. +(See Section~\ref{subsec:Examples-of-non-functors} for more examples.) +Let us now apply the raw tree encoding recipe to the \lstinline!Functor! typeclass. The result will be a new type constructor that we call a \textbf{free functor on} $F$.\index{free functor} Here $F$ is the effect constructor and does not need to be covariant with respect to its type parameter. -The recipe tells us to define a case class for the \inputencoding{latin9}\lstinline!fmap!\inputencoding{utf8} +The recipe tells us to define a case class for the \lstinline!fmap! method and another case class to wrap the given type constructor $F$. -So, the raw tree encoding of a free functor on $F$ looks like this:\inputencoding{latin9} +So, the raw tree encoding of a free functor on $F$ looks like this: \begin{lstlisting} sealed trait FFR[F[_], A] { def map[B](f: A => B): FFR[F, B] = FMap(f, this) @@ -2312,7 +2057,7 @@ \subsection{Free functors} final case class FMap[F[_], X, Y](f: X => Y, p: FFR[F, X]) extends FFR[F, Y] final case class Op[F[_], A](op: F[A]) extends FFR[F, A] \end{lstlisting} -\inputencoding{utf8} + This code corresponds to the following notation for the lifting $f^{\uparrow\text{FFR}}$: \begin{align} & \text{FFR}^{F^{\bullet},A}\triangleq F^{A}+\exists X.\,(X\rightarrow A)\times\text{FFR}^{F^{\bullet},X}\quad,\label{eq:definition-FFR-existential-type}\\ @@ -2324,25 +2069,24 @@ \subsection{Free functors} that the existentially quantified type must be assigned to a specific type every time we create a specific value. -A \textsf{``}free functor program\textsf{''} is a value of type \inputencoding{latin9}\lstinline!FFR[F, A]!\inputencoding{utf8}. -To construct such values, we need to begin with a wrapped \inputencoding{latin9}\lstinline!F!\inputencoding{utf8}-operation -followed by some \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -methods. To use a free functor program in practice, we need to apply -a runner to it. A simple runner is a function of type $\forall A.\,\text{FFR}^{F,A}\rightarrow A$ -that extracts a value of type $A$ from a free functor program. To -obtain a runner, we need to know how to extract values from the effect -constructor $F$. That information is given by a function of type -$\forall C.\,F^{C}\rightarrow C$ (an \index{effect runner}effect -runner for $F$). To implement such functions, we use the trait \inputencoding{latin9}\lstinline!Runner!\inputencoding{utf8} -defined in Section~\ref{subsec:A-first-recipe-monadic-dsl}. Now -we can write the code of the runner for $\text{FFR}^{F,A}$:\inputencoding{latin9} +A \textsf{``}free functor program\textsf{''} is a value of type \lstinline!FFR[F, A]!. +To construct such values, we need to begin with a wrapped \lstinline!F!-operation +followed by some \lstinline!map! methods. To use a free functor program +in practice, we need to apply a runner to it. A simple runner is a +function of type $\forall A.\,\text{FFR}^{F,A}\rightarrow A$ that +extracts a value of type $A$ from a free functor program. To obtain +a runner, we need to know how to extract values from the effect constructor +$F$. That information is given by a function of type $\forall C.\,F^{C}\rightarrow C$ +(an \index{effect runner}effect runner for $F$). To implement such +functions, we use the trait \lstinline!Runner! defined in Section~\ref{subsec:A-first-recipe-monadic-dsl}. +Now we can write the code of the runner for $\text{FFR}^{F,A}$: \begin{lstlisting} def runFFR[F[_], A](runner: Runner[F]): FFR[F, A] => A = { case FMap(f, p) => f(runFFR(runner)(p)) case Op(op) => runner.run(op) }*** check if this works \end{lstlisting} -\inputencoding{utf8}The code notation for this function is: +The code notation for this function is: \begin{align*} & \text{runFFR}^{F^{\bullet},A}:(\forall C.\,F^{C}\rightarrow C)\rightarrow\text{FFR}^{F^{\bullet},A}\rightarrow A\quad,\\ & \text{runFFR}(\text{run}:\forall C.\,F^{C}\rightarrow C)\triangleq\forall B.\,\,\begin{array}{|c||c|} @@ -2367,12 +2111,11 @@ \subsection{Free functors} \end{array}\quad. \end{align*} -The type constructor \inputencoding{latin9}\lstinline!FFR!\inputencoding{utf8} -is a \index{free functor!raw tree encoding}raw tree encoding of the -free functor and does not satisfy the functor laws. This is not a -problem in practice, because the functor laws will be satisfied after -we run an \inputencoding{latin9}\lstinline!FFR!\inputencoding{utf8}-program -into any lawful functor $G$: +The type constructor \lstinline!FFR! is a \index{free functor!raw tree encoding}raw +tree encoding of the free functor and does not satisfy the functor +laws. This is not a problem in practice, because the functor laws +will be satisfied after we run an \lstinline!FFR!-program into any +lawful functor $G$: \subsubsection{Statement \label{subsec:Statement-free-functor-raw-tree-encoding-satisfies-laws}\ref{subsec:Statement-free-functor-raw-tree-encoding-satisfies-laws}} @@ -2391,7 +2134,7 @@ \subsubsection{Statement \label{subsec:Statement-free-functor-raw-tree-encoding- \subparagraph{Proof} -Apply both sides of the laws to an arbitrary \inputencoding{latin9}\lstinline!FFR!\inputencoding{utf8}-program +Apply both sides of the laws to an arbitrary \lstinline!FFR!-program $p^{:\text{FFR}^{F^{\bullet},A}}$: \begin{align*} {\color{greenunder}\text{identity law}:}\quad & p\triangleright\text{id}^{\uparrow\text{FFR}}\bef\rho=p\triangleright\rho\quad,\\ @@ -2427,64 +2170,48 @@ \subsubsection{Statement \label{subsec:Statement-free-functor-raw-tree-encoding- To transform the raw tree encoding of the free functor into a reduced encoding, we require that all functor laws should hold even before applying a runner. We begin by finding out in detail why the functor -laws fail to hold for \inputencoding{latin9}\lstinline!FFR[F, A]!\inputencoding{utf8}. - -The type \inputencoding{latin9}\lstinline!FFR[F, A]!\inputencoding{utf8} -is a disjunction of two case classes, \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Op!\inputencoding{utf8}. The -functor\textsf{'}s composition law says that the composition of lifted functions, -$f^{\uparrow\text{FFR}}\bef g^{\uparrow\text{FFR}}$, must be equal -to the lifted composition: $(f\bef g)^{\uparrow\text{FFR}}$. If we -apply $(f\bef g)^{\uparrow\text{FFR}}$ to a value of the form \inputencoding{latin9}\lstinline!Op(op)!\inputencoding{utf8}, -we will get \inputencoding{latin9}\lstinline!FMap(f andThen g, Op(op))!\inputencoding{utf8}. +laws fail to hold for \lstinline!FFR[F, A]!. + +The type \lstinline!FFR[F, A]! is a disjunction of two case classes, +\lstinline!FMap! and \lstinline!Op!. The functor\textsf{'}s composition law +says that the composition of lifted functions, $f^{\uparrow\text{FFR}}\bef g^{\uparrow\text{FFR}}$, +must be equal to the lifted composition: $(f\bef g)^{\uparrow\text{FFR}}$. +If we apply $(f\bef g)^{\uparrow\text{FFR}}$ to a value of the form +\lstinline!Op(op)!, we will get \lstinline!FMap(f andThen g, Op(op))!. However, applying the composition $f^{\uparrow\text{FFR}}\bef g^{\uparrow\text{FFR}}$ -to \inputencoding{latin9}\lstinline!Op(op)!\inputencoding{utf8}, -we obtain a different value: \inputencoding{latin9}\lstinline!FMap(g, FMap(f, Op(op)))!\inputencoding{utf8}. -That value has nested \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8} -classes instead of the composition of \inputencoding{latin9}\lstinline!f!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!g!\inputencoding{utf8}. The -composition law would hold if the \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -method created a non-nested \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8} -class containing the composition of \inputencoding{latin9}\lstinline!f!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!g!\inputencoding{utf8}. To achieve -that, let us define \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -on the \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8} -case class like this:\inputencoding{latin9} +to \lstinline!Op(op)!, we obtain a different value: \lstinline!FMap(g, FMap(f, Op(op)))!. +That value has nested \lstinline!FMap! classes instead of the composition +of \lstinline!f! and \lstinline!g!. The composition law would hold +if the \lstinline!map! method created a non-nested \lstinline!FMap! +class containing the composition of \lstinline!f! and \lstinline!g!. +To achieve that, let us define \lstinline!map! on the \lstinline!FMap! +case class like this: \begin{lstlisting} final case class FMap[F[_], X, Y](f: X => Y, p: FFR[F, X]) extends FFR[F, Y] { def map[Z](g: Y => Z): FFR[F, Z] = FMap[F, X, Z](f andThen g, p) } \end{lstlisting} -\inputencoding{utf8} + Turn now to the functor\textsf{'}s identity law, which says that the lifted identity function, $\text{id}^{\uparrow\text{FFR}}$, must be again -an identity function. The new definition of \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -for the \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8} -case class satisfies that law. However, applying $\text{id}^{\uparrow\text{FFR}}$ -to a value \inputencoding{latin9}\lstinline!Op(op)!\inputencoding{utf8} -does not return \inputencoding{latin9}\lstinline!Op(op)!\inputencoding{utf8} -but instead gives \inputencoding{latin9}\lstinline!FMap(identity, Op(op))!\inputencoding{utf8}. -Values of the form \inputencoding{latin9}\lstinline!Op(op)!\inputencoding{utf8} -represent \inputencoding{latin9}\lstinline!F!\inputencoding{utf8}-effects. +an identity function. The new definition of \lstinline!map! for the +\lstinline!FMap! case class satisfies that law. However, applying +$\text{id}^{\uparrow\text{FFR}}$ to a value \lstinline!Op(op)! does +not return \lstinline!Op(op)! but instead gives \lstinline!FMap(identity, Op(op))!. +Values of the form \lstinline!Op(op)! represent \lstinline!F!-effects. The functor identity law would hold if we instead represented $F$-effects -by the \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8} -case class as \inputencoding{latin9}\lstinline!FMap(identity, Op(op))!\inputencoding{utf8}. - -Any \inputencoding{latin9}\lstinline!FFR!\inputencoding{utf8}-program -must have the form \inputencoding{latin9}\lstinline!Op(x).map(y).map(z)...!\inputencoding{utf8}, -having zero or more \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -methods. If we represent \inputencoding{latin9}\lstinline!F!\inputencoding{utf8}-effects -by \inputencoding{latin9}\lstinline!FMap(identity, Op(op))!\inputencoding{utf8} -and implement the \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} -methods for \inputencoding{latin9}\lstinline!FMap!\inputencoding{utf8} -as shown above, it will follow that \emph{all} \inputencoding{latin9}\lstinline!FFR!\inputencoding{utf8}-programs -always have the form \inputencoding{latin9}\lstinline!FMap(f, Op(op))!\inputencoding{utf8} -for some function \inputencoding{latin9}\lstinline!f!\inputencoding{utf8}. -So, we may remove the \inputencoding{latin9}\lstinline!Op!\inputencoding{utf8} +by the \lstinline!FMap! case class as \lstinline!FMap(identity, Op(op))!. + +Any \lstinline!FFR!-program must have the form \lstinline!Op(x).map(y).map(z)...!, +having zero or more \lstinline!map! methods. If we represent \lstinline!F!-effects +by \lstinline!FMap(identity, Op(op))! and implement the \lstinline!map! +methods for \lstinline!FMap! as shown above, it will follow that +\emph{all} \lstinline!FFR!-programs always have the form \lstinline!FMap(f, Op(op))! +for some function \lstinline!f!. So, we may remove the \lstinline!Op! case class altogether. The result is a simplified definition of the free functor. The complete -code is:\inputencoding{latin9} +code is: \begin{lstlisting} sealed trait FF[F[_], A] { def map[B](f: A => B): FF[F, B] @@ -2496,7 +2223,7 @@ \subsubsection{Statement \label{subsec:Statement-free-functor-raw-tree-encoding- case FMap(f, p) => f(runner.run(p)) }*** check if this works \end{lstlisting} -\inputencoding{utf8} + The code notation for this code and a general runner is: \begin{align*} & \text{FF}^{F^{\bullet},A}\triangleq\exists C.\,\left(C\rightarrow A\right)\times F^{C}\quad,\quad\quad(\exists C.\,f^{:C\rightarrow A}\times p^{:F^{C}})\triangleright(g^{:A\rightarrow B})^{\uparrow\text{FR}}\triangleq\exists^{C}.\,(f\bef g)\times p\quad,\\ @@ -2505,63 +2232,60 @@ \subsubsection{Statement \label{subsec:Statement-free-functor-raw-tree-encoding- \end{align*} This is the \textbf{reduced encoding}\index{free functor!reduced encoding} of the free functor on $F$. This encoding was derived by imposing -the functor laws, so those laws will hold for values of type \inputencoding{latin9}\lstinline!FF[F, A]!\inputencoding{utf8} +the functor laws, so those laws will hold for values of type \lstinline!FF[F, A]! even before applying a runner. -The free functor construction \inputencoding{latin9}\lstinline!FF[F, A]!\inputencoding{utf8} -converts \emph{any} type constructor \inputencoding{latin9}\lstinline!F[_]!\inputencoding{utf8} -into a lawful functor. What if \inputencoding{latin9}\lstinline!F[_]!\inputencoding{utf8} -is already a functor? It turns out that the type \inputencoding{latin9}\lstinline!FF[F, A]!\inputencoding{utf8} -will then be \emph{equivalent} to \inputencoding{latin9}\lstinline!F[A]!\inputencoding{utf8}, -assuming that all code that uses \inputencoding{latin9}\lstinline!FF[F, A]!\inputencoding{utf8} -is fully parametric. This property of the reduced encoding is called -the \textbf{co-Yoneda identity}\index{co-Yoneda identity}: +The free functor construction \lstinline!FF[F, A]! converts \emph{any} +type constructor \lstinline!F[_]! into a lawful functor. What if +\lstinline!F[_]! is already a functor? It turns out that the type +\lstinline!FF[F, A]! will then be \emph{equivalent} to \lstinline!F[A]!, +assuming that all code that uses \lstinline!FF[F, A]! is fully parametric. +This property of the reduced encoding is called the \textbf{co-Yoneda +identity}\index{co-Yoneda identity}: \begin{align*} {\color{greenunder}\text{covariant co-Yoneda identity}:}\quad & \exists C.\,\left(C\rightarrow A\right)\times F^{C}\cong F^{A}\quad\text{for any functor }F\quad. \end{align*} We will prove this type equivalence in Statement~\ref{subsec:Statement-co-Yoneda-two-identities} below. -We conclude that the reduced encoding of the free functor (\inputencoding{latin9}\lstinline!FF[F, A]!\inputencoding{utf8}) -has advantages over the raw tree encoding (\inputencoding{latin9}\lstinline!FFR[F, A]!\inputencoding{utf8}). +We conclude that the reduced encoding of the free functor (\lstinline!FF[F, A]!) +has advantages over the raw tree encoding (\lstinline!FFR[F, A]!). The reduced encoding contains only one case class, and the runner -code is not recursive because an \inputencoding{latin9}\lstinline!FF!\inputencoding{utf8}-program -does not contain any nested case classes. If $F$ is already a functor, -the reduced encoding of a free functor on $F$ is equivalent to $F$. +code is not recursive because an \lstinline!FF!-program does not +contain any nested case classes. If $F$ is already a functor, the +reduced encoding of a free functor on $F$ is equivalent to $F$. A disadvantage of the reduced encoding is that the function composition -is done in the code \inputencoding{latin9}\lstinline!FMap(f andThen g, p)!\inputencoding{utf8}. -The Scala compiler cannot directly handle the composition of a large -number of functions without causing a stack overflow. This problem -can be resolved if we postpone the function composition and instead -create a list of functions that need to be composed. The runner can -evaluate the list of functions without running into a stack overflow. +is done in the code \lstinline!FMap(f andThen g, p)!. The Scala compiler +cannot directly handle the composition of a large number of functions +without causing a stack overflow. This problem can be resolved if +we postpone the function composition and instead create a list of +functions that need to be composed. The runner can evaluate the list +of functions without running into a stack overflow. This gives us an idea for another encoding of the stack-safe free -functor, which we will call \inputencoding{latin9}\lstinline!FFS!\inputencoding{utf8}. -First, we implement a data structure called \inputencoding{latin9}\lstinline!FuncSeq!\inputencoding{utf8} -for storing a list of functions with matching types. A value of type -\inputencoding{latin9}\lstinline!FuncSeq[A, B]!\inputencoding{utf8} +functor, which we will call \lstinline!FFS!. First, we implement +a data structure called \lstinline!FuncSeq! for storing a list of +functions with matching types. A value of type \lstinline!FuncSeq[A, B]! holds a list of functions with types $A\rightarrow C_{1}$, $C_{1}\rightarrow C_{2}$, ..., $C_{n-1}\rightarrow C_{n}$, $C_{n}\rightarrow B$, where $C_{i}$ are some chosen types. A list of with those types can be composed to yield a function of type $A\rightarrow B$. To simplify code, we -will cast all intermediate types to \inputencoding{latin9}\lstinline!Any!\inputencoding{utf8} -and back. Our code will take care to construct \inputencoding{latin9}\lstinline!FuncSeq!\inputencoding{utf8} -values with correct types.\inputencoding{latin9}\lstinline!final case class FuncSeq[X, Y](first: X => Any, funcs: Vector[Any => Any]) { def append[Z](g: Y => Z): FuncSeq[X, Z] = FuncSeq(first, funcs :+ g.asInstanceOf[Any => Any])}!\inputencoding{utf8} +will cast all intermediate types to \lstinline!Any! and back. Our +code will take care to construct \lstinline!FuncSeq! values with +correct types.\lstinline!final case class FuncSeq[X, Y](first: X => Any, funcs: Vector[Any => Any]) { def append[Z](g: Y => Z): FuncSeq[X, Z] = FuncSeq(first, funcs :+ g.asInstanceOf[Any => Any])}! -To ensure stack safety when working with \inputencoding{latin9}\lstinline!FuncSeq!\inputencoding{utf8}, -we implement a tail-recursive function \inputencoding{latin9}\lstinline!runSeq!\inputencoding{utf8} -that composes all functions stored in the sequence and applies them -to a given value. +To ensure stack safety when working with \lstinline!FuncSeq!, we +implement a tail-recursive function \lstinline!runSeq! that composes +all functions stored in the sequence and applies them to a given value. -\inputencoding{latin9}\begin{lstlisting} +\begin{lstlisting} @tailrec def runSeq[X, Y](x: X, p: FuncSeq[X, Y]): Y = p.funcs.headOption match { case None => p.first(x).asInstanceOf[Y] case Some(second) => runSeq(p.first(x), FuncSeq(second, p.funcs.tail)) } \end{lstlisting} -\inputencoding{utf8}Now we can write the code of the free functor \inputencoding{latin9}\lstinline!FFS!\inputencoding{utf8}:\inputencoding{latin9} +Now we can write the code of the free functor \lstinline!FFS!: \begin{lstlisting} sealed trait FFS[F[_], A] { def map[B](f: A => B): FFS[F, B] @@ -2573,7 +2297,7 @@ \subsubsection{Statement \label{subsec:Statement-free-functor-raw-tree-encoding- case FMap(f, p) => runSeq(runner.run(p), f) }*** check if this works \end{lstlisting} -\inputencoding{utf8} + \subsection{Free contrafunctors} @@ -2614,35 +2338,29 @@ \subsection{Free constructions that assume other typeclasses} The first example is the free monoid on $T$ if $T$ is already a semigroup. The only thing missing in a semigroup compared with a monoid is the empty element: a semigroup does not necessarily have one. We -also notice that the difference between the free monoid (\inputencoding{latin9}\lstinline!List[T]!\inputencoding{utf8}) -and the free semigroup (\inputencoding{latin9}\lstinline!NEL[T]!\inputencoding{utf8}) -is just the empty element: $\text{List}^{T}=\bbnum 1+\text{NEL}^{T}$. -Motivated by these considerations, we define the free monoid on $T$ -as $\bbnum 1+T$ when $T$ is already a semigroup. Indeed, we may -implement the monoid instance:\inputencoding{latin9} +also notice that the difference between the free monoid (\lstinline!List[T]!) +and the free semigroup (\lstinline!NEL[T]!) is just the empty element: +$\text{List}^{T}=\bbnum 1+\text{NEL}^{T}$. Motivated by these considerations, +we define the free monoid on $T$ as $\bbnum 1+T$ when $T$ is already +a semigroup. Indeed, we may implement the monoid instance: \begin{lstlisting} def monoidOnSemi[T: Semigroup]: Monoid[Option[T]] = Monoid( (l, r) => (l zip r).map { case (x, y) => x |+| y }, None) *** verify code \end{lstlisting} -\inputencoding{utf8} + The second example is found by considering the free monad encoding -\inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8} (see -Section~\ref{subsec:Motivation-free-monad-different-encodings}). -The encoding \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8} -can be seen as \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8} -applied to the free functor \inputencoding{latin9}\lstinline!FF[F, A]!\inputencoding{utf8}. -If the effect constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -is already functor, the free functor \inputencoding{latin9}\lstinline!FF[F, A]!\inputencoding{utf8} -is equivalent to just \inputencoding{latin9}\lstinline!F[A]!\inputencoding{utf8}. -So, the free monad on a functor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -is the encoding \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8}. -It is a simpler data structure than \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8}, -the free monad on an arbitrary type constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8}. - -It also follows that the free monad encodings \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Free2!\inputencoding{utf8} are -equivalent when the effect constructor \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} +\lstinline!Free2! (see Section~\ref{subsec:Motivation-free-monad-different-encodings}). +The encoding \lstinline!Free2! can be seen as \lstinline!Free1! +applied to the free functor \lstinline!FF[F, A]!. If the effect constructor +\lstinline!F! is already functor, the free functor \lstinline!FF[F, A]! +is equivalent to just \lstinline!F[A]!. So, the free monad on a functor +\lstinline!F! is the encoding \lstinline!Free1!. It is a simpler +data structure than \lstinline!Free2!, the free monad on an arbitrary +type constructor \lstinline!F!. + +It also follows that the free monad encodings \lstinline!Free1! and +\lstinline!Free2! are equivalent when the effect constructor \lstinline!F! is a functor. The free functor construction can be viewed as a building block for @@ -2654,34 +2372,27 @@ \subsection{Free constructions that assume other typeclasses} typeclasses on functors. To gain some intuition about how to build typeclasses based on functors, -let us examine the definition of the free monad encoding \inputencoding{latin9}\lstinline!Free1[F, A]!\inputencoding{utf8}. -(That definition assumes that \inputencoding{latin9}\lstinline!F!\inputencoding{utf8} -is a functor.) A monad may be defined as a functor that additionally -has the \inputencoding{latin9}\lstinline!pure!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} -methods satisfying suitable laws (the equivalence of \inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!flatMap!\inputencoding{utf8} -was proved in Statement~\ref{subsec:Statement-flatten-equivalent-to-flatMap}). -The raw tree encoding for a monad\textsf{'}s definition via \inputencoding{latin9}\lstinline!pure!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} -gives:\inputencoding{latin9} +let us examine the definition of the free monad encoding \lstinline!Free1[F, A]!. +(That definition assumes that \lstinline!F! is a functor.) A monad +may be defined as a functor that additionally has the \lstinline!pure! +and \lstinline!flatten! methods satisfying suitable laws (the equivalence +of \lstinline!flatten! and \lstinline!flatMap! was proved in Statement~\ref{subsec:Statement-flatten-equivalent-to-flatMap}). +The raw tree encoding for a monad\textsf{'}s definition via \lstinline!pure! +and \lstinline!flatten! gives: \begin{lstlisting} sealed trait Free5[F[_], A] final case class Pure[F[_], A](a: A) extends Free5[F, A] final case class Wrap[F[_], A](fa: F[A]) extends Free5[F, A] final case class Flatten[F[_], A](ff: Free5[F, Free5[F, A]]) extends Free5[F, A] \end{lstlisting} -\inputencoding{utf8}This code differs from \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8} -in two ways. First, \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8} -does not use a \inputencoding{latin9}\lstinline!Wrap!\inputencoding{utf8} -case class. Second, \inputencoding{latin9}\lstinline!Free5!\inputencoding{utf8} -has a recursive type definition of the form \inputencoding{latin9}\lstinline!Free5[F, Free5[F, A]]!\inputencoding{utf8} -that uses \inputencoding{latin9}\lstinline!Free5!\inputencoding{utf8} -twice, while \inputencoding{latin9}\lstinline!Free1!\inputencoding{utf8} -instead uses a simpler type: \inputencoding{latin9}\lstinline!F[Free1[F, A]]!\inputencoding{utf8}. -{*}{*}{*} We avoid the simplification Free5{[}F, F{[}A{]}{]} because -this creates a recursive definition of Free5{[}F, A{]} whose recursive -use modifies the type parameter A of Free5 +This code differs from \lstinline!Free1! in two ways. First, \lstinline!Free1! +does not use a \lstinline!Wrap! case class. Second, \lstinline!Free5! +has a recursive type definition of the form \lstinline!Free5[F, Free5[F, A]]! +that uses \lstinline!Free5! twice, while \lstinline!Free1! instead +uses a simpler type: \lstinline!F[Free1[F, A]]!. {*}{*}{*} We avoid +the simplification Free5{[}F, F{[}A{]}{]} because this creates a recursive +definition of Free5{[}F, A{]} whose recursive use modifies the type +parameter A of Free5 \subsection{Free pointed functors} @@ -2713,10 +2424,9 @@ \subsection{Church encodings of free typeclasses} \[ \forall X^{:\text{MyTypeclass}}.\,(A\rightarrow X)\rightarrow X \] -is the free \inputencoding{latin9}\lstinline!MyTypeclass!\inputencoding{utf8} -in the lawful Church encoding. The laws hold! This is more economical -than the raw tree encoding, see the \inputencoding{latin9}\lstinline!Semigroup!\inputencoding{utf8} -example. +is the free \lstinline!MyTypeclass! in the lawful Church encoding. +The laws hold! This is more economical than the raw tree encoding, +see the \lstinline!Semigroup! example. \section{Laws of free constructions} @@ -2735,27 +2445,26 @@ \subsection{Free constructions for $P$-typeclasses\label{subsec:Free-constructio Some features are common to all the free typeclass constructions we have seen. We begin by generalizing the features of the free monoid construction to other similar typeclasses. We will then extend the -results to typeclasses for type constructors (such as \inputencoding{latin9}\lstinline!Functor!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!Monad!\inputencoding{utf8}). +results to typeclasses for type constructors (such as \lstinline!Functor! +and \lstinline!Monad!). The free monoid is a type constructor ($\text{FM}^{\bullet}$) that transforms an arbitrary type $T$ into a new type ($\text{FM}^{T}$) -having a \inputencoding{latin9}\lstinline!Monoid!\inputencoding{utf8} -typeclass instance. Values of type $T$ can be wrapped into values -of type $\text{FM}^{T}$. For any given monoid $M$ and a given function -of type $T\rightarrow M$, a \textsf{``}free monoid program\textsf{''} (i.e., a value -of type $\text{FM}^{T}$) can be \textsf{``}run into $M$\textsf{''}. The resulting -runner (of type $\text{FM}^{T}\rightarrow M$) will preserve the monoid -operations between $\text{FM}^{T}$ and $M$. So, the monoid laws -will hold after running a \textsf{``}free monoid program\textsf{''}, even when the -chosen encoding $\text{FM}^{T}$ violates some of the monoid laws. +having a \lstinline!Monoid! typeclass instance. Values of type $T$ +can be wrapped into values of type $\text{FM}^{T}$. For any given +monoid $M$ and a given function of type $T\rightarrow M$, a \textsf{``}free +monoid program\textsf{''} (i.e., a value of type $\text{FM}^{T}$) can be +\textsf{``}run into $M$\textsf{''}. The resulting runner (of type $\text{FM}^{T}\rightarrow M$) +will preserve the monoid operations between $\text{FM}^{T}$ and $M$. +So, the monoid laws will hold after running a \textsf{``}free monoid program\textsf{''}, +even when the chosen encoding $\text{FM}^{T}$ violates some of the +monoid laws. To generalize from monoids to other typeclasses, it helps to use the notion of a \textsf{``}$P$-typeclass\textsf{''} introduced in Section~\ref{subsec:P-algebraic-typeclasses}. For a given (covariant) functor $P$, a $P$\textbf{-typeclass} \index{$P$-typeclass} has the evidence data equivalent to a value of type $P^{A}\rightarrow A$. -For the \inputencoding{latin9}\lstinline!Monoid!\inputencoding{utf8} -typeclass, the structure functor is $P^{A}\triangleq\bbnum 1+A\times A$, +For the \lstinline!Monoid! typeclass, the structure functor is $P^{A}\triangleq\bbnum 1+A\times A$, and the evidence data has type $A\times\left(A\times A\rightarrow A\right)$, which is equivalent to $P^{A}\rightarrow A$. The two parts of the disjunctive type $\bbnum 1+A\times A$ correspond to the \emph{arguments} @@ -3032,7 +2741,7 @@ \subsubsection{Statement \label{subsec:Statement-some-properties-of-free-P-typec admit encodings that obey only a subset of typeclass laws (possibly, no laws at all). -The next step is to generalize the raw tree encoding from \inputencoding{latin9}\lstinline!Monoid!\inputencoding{utf8} +The next step is to generalize the raw tree encoding from \lstinline!Monoid! to an arbitrary $P$-typeclass and to show that it satisfies the definition of a \textsf{``}free $P$-typeclass encoding\textsf{''}. Although the raw tree encoding does not satisfy any typeclass laws, it gives us a valid and usable @@ -3066,9 +2775,9 @@ \subsubsection{Statement \label{subsec:Statement-some-properties-of-free-P-typec \subsubsection{Statement \label{subsec:Statement-free-P-typeclass-raw-tree-encoding}\ref{subsec:Statement-free-P-typeclass-raw-tree-encoding}} The free monad on $P$, denoted by $\text{FPR}^{T}\triangleq T+P^{\text{FPR}^{T}}$, -is a free $P$-typeclass encoding. The monad\textsf{'}s \inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} -method is the same as \inputencoding{latin9}\lstinline!eval!\inputencoding{utf8} -applied to an argument of type \inputencoding{latin9}\lstinline!FPR[FPR[T]]!\inputencoding{utf8}. +is a free $P$-typeclass encoding. The monad\textsf{'}s \lstinline!flatten! +method is the same as \lstinline!eval! applied to an argument of +type \lstinline!FPR[FPR[T]]!. \subparagraph{Proof} @@ -3117,8 +2826,7 @@ \subsubsection{Statement \label{subsec:Statement-free-P-typeclass-raw-tree-encod To verify the right identity law~(\ref{eq:free-typeclass-encoding-right-identity-law}), we first show that the function $\text{eval}_{\text{FPR}}^{\text{FPR}^{T}}:\text{FPR}^{\text{FPR}^{T}}\rightarrow\text{FPR}^{T}$ -is the same as the monad\textsf{'}s \inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} -method for \inputencoding{latin9}\lstinline!FPR!\inputencoding{utf8} +is the same as the monad\textsf{'}s \lstinline!flatten! method for \lstinline!FPR! given by the following code matrix (see Statement~\ref{subsec:Statement-monad-construction-4-free-monad}): \[ \text{ftn}_{\text{FPR}}=\,\begin{array}{|c||cc|} @@ -3128,8 +2836,8 @@ \subsubsection{Statement \label{subsec:Statement-free-P-typeclass-raw-tree-encod P^{\text{FPR}^{\text{FPR}^{T}}} & \bbnum 0 & \overline{\text{ftn}}_{\text{FPR}}^{\uparrow P} \end{array}\quad. \] -We use the definition of \inputencoding{latin9}\lstinline!eval!\inputencoding{utf8} -given in \textbf{(a)} with the type $\text{FPR}^{T}$ instead of $T$: +We use the definition of \lstinline!eval! given in \textbf{(a)} with +the type $\text{FPR}^{T}$ instead of $T$: \[ \text{eval}_{\text{FPR}}^{\text{FPR}^{T}}=\,\begin{array}{|c||c|} & \text{FPR}^{T}\\ @@ -3238,25 +2946,21 @@ \subsubsection{Statement \label{subsec:Statement-free-P-typeclass-raw-tree-encod So, the code matrices for $f$ and for $\text{eval}_{\text{FPR}}^{C}$ are equal. $\square$ -As we will now show, \inputencoding{latin9}\lstinline!FPR!\inputencoding{utf8}\textsf{'}s -\inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} function +As we will now show, \lstinline!FPR!\textsf{'}s \lstinline!flatten! function is a $P$-algebra morphism; in other words, it preserves the $P$-typeclass operations. So, it is not useful to apply the free $P$-typeclass construction twice, as the result can be reduced to a single layer -of \inputencoding{latin9}\lstinline!FPR[T]!\inputencoding{utf8} while -preserving the operations. +of \lstinline!FPR[T]! while preserving the operations. \subsubsection{Statement \label{subsec:Statement-free-P-typeclass-monad}\ref{subsec:Statement-free-P-typeclass-monad}} -The free monad\textsf{'}s \inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} -function, $\text{ftn}_{\text{FPR}}:\text{FPR}^{\text{FPR}^{T}}\rightarrow\text{FPR}^{T}$, +The free monad\textsf{'}s \lstinline!flatten! function, $\text{ftn}_{\text{FPR}}:\text{FPR}^{\text{FPR}^{T}}\rightarrow\text{FPR}^{T}$, is a $P$-algebra morphism. \subparagraph{Proof} By Statement~\ref{subsec:Statement-free-P-typeclass-raw-tree-encoding}, -the \inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} -function can be expressed as $\text{eval}_{\text{FPR}}^{\text{FPR}^{T}}$. +the \lstinline!flatten! function can be expressed as $\text{eval}_{\text{FPR}}^{\text{FPR}^{T}}$. By the same statement, $\text{eval}_{\text{FPR}}^{C}$ is always a $P$-algebra morphism as long as $C$ is a $P$-algebra obeying all the laws of $\text{FPR}^{T}$. So, we may use $C=\text{FPR}^{T}$ @@ -3270,10 +2974,10 @@ \subsubsection{Statement \label{subsec:Statement-free-P-typeclass-monad}\ref{sub \subsubsection{Statement \label{subsec:Statement-free-typeclass-encoding-is-a-monad}\ref{subsec:Statement-free-typeclass-encoding-is-a-monad}} Any free $P$-typeclass encoding $E$ satisfying Definition~\ref{subsec:Definition-free-P-typeclass-encoding} -is a monad. The monad $E$\textsf{'}s \inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} -function ($\text{ftn}_{E}$) is equal to the unique $P$-algebra morphism -$\text{eval}_{E}^{E^{A}}$ between $P$-algebras $E^{E^{A}}$ and -$E^{A}$. The $P$-algebra morphism law of $\text{ftn}_{E}$ is: +is a monad. The monad $E$\textsf{'}s \lstinline!flatten! function ($\text{ftn}_{E}$) +is equal to the unique $P$-algebra morphism $\text{eval}_{E}^{E^{A}}$ +between $P$-algebras $E^{E^{A}}$ and $E^{A}$. The $P$-algebra +morphism law of $\text{ftn}_{E}$ is: \begin{wrapfigure}{l}{0.2\columnwidth}% \vspace{-2.15\baselineskip} @@ -3296,8 +3000,7 @@ \subsubsection{Statement \label{subsec:Statement-free-typeclass-encoding-is-a-mo We need to implement the monad\textsf{'}s methods $\text{pu}_{E}$ and $\text{ftn}_{E}$ and show that the monad laws hold. The method $\text{pu}_{E}$ exists by Definition~\ref{subsec:Definition-free-P-typeclass-encoding}(b). -The \inputencoding{latin9}\lstinline!flatten!\inputencoding{utf8} -method ($\text{ftn}_{E}$) is defined as: +The \lstinline!flatten! method ($\text{ftn}_{E}$) is defined as: \[ \text{ftn}_{E}:E^{E^{A}}\rightarrow E^{A}\quad,\quad\quad\text{ftn}_{E}\triangleq\text{eval}_{E}^{E^{A}}\quad. \] @@ -3381,9 +3084,9 @@ \subsection{Describing laws of $P$-typeclasses as values\label{subsec:Describing The data in the pair $(\oplus_{_{A}},e_{_{A}})$ is a value of type $P^{A}\rightarrow A$, where $P^{A}\triangleq\bbnum 1+A\times A$ -for the \inputencoding{latin9}\lstinline!Monoid!\inputencoding{utf8} -typeclass (but $P$ will be different for other typeclasses). So, -the functions $f_{1}$ and $f_{2}$ have type $(P^{A}\rightarrow A)\times A\times A\times A\rightarrow A$. +for the \lstinline!Monoid! typeclass (but $P$ will be different +for other typeclasses). So, the functions $f_{1}$ and $f_{2}$ have +type $(P^{A}\rightarrow A)\times A\times A\times A\rightarrow A$. The pair $(f_{1},f_{2})$ is equivalent to a single value of type $(P^{A}\rightarrow A)\times A\times A\times A\rightarrow A\times A$. Each law corresponds to a specific value of that type, which we will @@ -3404,7 +3107,7 @@ \subsection{Describing laws of $P$-typeclasses as values\label{subsec:Describing function $f$ of type $\text{Int}\rightarrow A$. Instead of values $x$, $y$, $z$ we will then use the values $f(1)$, $f(2)$, $f(3)$, etc., which are arbitrary values of type $A$ since $f$ is an arbitrary -function. This allows us to generalize the definition of the \inputencoding{latin9}\lstinline!Monoid!\inputencoding{utf8} +function. This allows us to generalize the definition of the \lstinline!Monoid! laws to other typeclasses of similar form: \subsubsection{Definition \label{subsec:Definition-law-function-P-typeclass}\ref{subsec:Definition-law-function-P-typeclass}} @@ -3414,9 +3117,9 @@ \subsubsection{Definition \label{subsec:Definition-law-function-P-typeclass}\ref \begin{equation} \text{LawF}_{P}\triangleq\forall A.\,(P^{A}\rightarrow A)\times(\text{Int}\rightarrow A)\rightarrow A\times A\quad.\label{eq:P-typeclass-law-type} \end{equation} -Given a function $l$ of type \inputencoding{latin9}\lstinline!LawF!\inputencoding{utf8}, -we say that an evidence value $p_{T}:P^{T}\rightarrow T$ \textbf{satisfies -the $l$-law} if: +Given a function $l$ of type \lstinline!LawF!, we say that an evidence +value $p_{T}:P^{T}\rightarrow T$ \textbf{satisfies the $l$-law} +if: \begin{align} {\color{greenunder}p_{T}\text{ satisfies the }l\text{-law}:}\quad & \text{for all }f^{:\text{Int}\rightarrow T}\quad:\quad\big(l^{T}(p_{T},f)\big)\triangleright\pi_{1}=\big(l^{T}(p_{T},f)\big)\triangleright\pi_{2}\quad.\label{eq:P-typeclass-law} \end{align} @@ -3442,111 +3145,100 @@ \subsubsection{Definition \label{subsec:Definition-law-function-P-typeclass}\ref type $T$. The result will be two values of type $T$; the law holds if these two values are equal. -To see in detail how this approach works, consider again the \inputencoding{latin9}\lstinline!Monoid!\inputencoding{utf8} +To see in detail how this approach works, consider again the \lstinline!Monoid! typeclass with its three laws. Suppose the type $T$ already has the monoid methods ($\oplus_{_{T}}$ and $e_{_{T}}$), and we would like to check whether the monoid laws hold for $T$. We will use the raw -tree encoding of the free monoid on $T$, that is, the type \inputencoding{latin9}\lstinline!FMR[T]!\inputencoding{utf8} +tree encoding of the free monoid on $T$, that is, the type \lstinline!FMR[T]! defined in Section~\ref{subsec:Free-monoids}. Begin with the left identity law of monoids: it says that for any value $t^{:T}$ we must have $e_{_{T}}\oplus_{_{T}}t=t$. The two sides of that law are two expressions involving the monoid $T$\textsf{'}s operations as well as an arbitrary value $t$ of type $T$. We first consider those two expressions as unevaluated expression trees. Those \textsf{``}law expression trees\textsf{''} are -two values of the type \inputencoding{latin9}\lstinline!FMR[T]!\inputencoding{utf8} -that we may implement in Scala code like this:\inputencoding{latin9} +two values of the type \lstinline!FMR[T]! that we may implement in +Scala code like this: \begin{lstlisting} def lhs1[T](t: T): FMR[T] = Combine(Empty(), Wrap(t)) def rhs1[T](t: T): FMR[T] = Wrap(t) \end{lstlisting} -\inputencoding{utf8}The two sides of the law must be defined as \emph{functions} of type +The two sides of the law must be defined as \emph{functions} of type $T\rightarrow\text{FMR}^{T}$ because the law should hold for arbitrary values $t^{:T}$. In order to verify that the law holds, we now need -to apply the \inputencoding{latin9}\lstinline!runner!\inputencoding{utf8} -function to the values \inputencoding{latin9}\lstinline!lhs1!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!rhs1!\inputencoding{utf8}. To -evaluate the expression trees of type $\text{FMR}^{T}$ into values -of type $T$, we specify an identity function (of type $T\rightarrow T$) -as the first argument of the \inputencoding{latin9}\lstinline!runner!\inputencoding{utf8} -function:\inputencoding{latin9} +to apply the \lstinline!runner! function to the values \lstinline!lhs1! +and \lstinline!rhs1!. To evaluate the expression trees of type $\text{FMR}^{T}$ +into values of type $T$, we specify an identity function (of type +$T\rightarrow T$) as the first argument of the \lstinline!runner! +function: \begin{lstlisting} def lhs1Evaluated[T: Monoid](t: T): T = runner[T, T](identity)(lhs1(t)) def rhs1Evaluated[T: Monoid](t: T): T = runner[T, T](identity)(rhs1(t)) \end{lstlisting} -\inputencoding{utf8}The law says that both sides should be equal as values of type $T$. -We could test that:\inputencoding{latin9} +The law says that both sides should be equal as values of type $T$. +We could test that: \begin{lstlisting} forAll { t: T => // Using the scalacheck library. lhs1Evaluated(t) shouldEqual rhs1Evaluated(t) } \end{lstlisting} -\inputencoding{utf8} + The right identity law is treated similarly. Turning now to the associativity law, we find that we need \emph{three} arbitrary values of type $T$, -so the \textsf{``}law expression trees\textsf{''} are:\inputencoding{latin9} +so the \textsf{``}law expression trees\textsf{''} are: \begin{lstlisting} def lhs3[T](t1: T, t2: T, t3: T): FMR[T] = Combine(Combine(Wrap(t1), Wrap(t2)), Wrap(t3)) def rhs3[T](t1: T, t2: T, t3: T): FMR[T] = Combine(Wrap(t1), Combine(Wrap(t2), Wrap(t3))) \end{lstlisting} -\inputencoding{utf8}To verify that the law holds for $T$, we again evaluate both expression -trees into values of type $T$ using the \inputencoding{latin9}\lstinline!runner!\inputencoding{utf8} -function:\inputencoding{latin9} +To verify that the law holds for $T$, we again evaluate both expression +trees into values of type $T$ using the \lstinline!runner! function: \begin{lstlisting} def lhs3Evaluated[T: Monoid](t1: T, t2: T, t3: T): T = runner[T, T](identity)(lhs3(t1, t2, t3)) def rhs3Evaluated[T: Monoid](t1: T, t2: T, t3: T): T = runner[T, T](identity)(rhs3(t1, t2, t3)) \end{lstlisting} -\inputencoding{utf8}To test that the law holds:\inputencoding{latin9} +To test that the law holds: \begin{lstlisting} forAll { (t1: T, t2: T, t3: T) => // Using the scalacheck library. lhs3Evaluated(t1, t2, t3) shouldEqual rhs3Evaluated(t1, t2, t3) } \end{lstlisting} -\inputencoding{utf8} + We can now generalize the type of \textsf{``}law expression trees\textsf{''} from -\inputencoding{latin9}\lstinline!Monoid!\inputencoding{utf8} to an -arbitrary $P$-typeclass. The two sides of the law are functions that -take as arguments one or more arbitrary values of type $T$ and will -return a value of type $\text{FPR}^{T}$. In order to be able to describe -any number of arbitrary values of type $T$, we use an arbitrary function -of type $\text{Int}\rightarrow T$. So, the law is expressed by a -function of type $(\text{Int}\rightarrow T)\rightarrow\text{FPR}^{T}\times\text{FPR}^{T}$. +\lstinline!Monoid! to an arbitrary $P$-typeclass. The two sides +of the law are functions that take as arguments one or more arbitrary +values of type $T$ and will return a value of type $\text{FPR}^{T}$. +In order to be able to describe any number of arbitrary values of +type $T$, we use an arbitrary function of type $\text{Int}\rightarrow T$. +So, the law is expressed by a function of type $(\text{Int}\rightarrow T)\rightarrow\text{FPR}^{T}\times\text{FPR}^{T}$. Since the law is supposed to work in the same way for all types $T$, we add a universal quantifier ($\forall T$). The type of \textsf{``}pairs -of law expression trees\textsf{''} (\inputencoding{latin9}\lstinline!LawET!\inputencoding{utf8}) -becomes: +of law expression trees\textsf{''} (\lstinline!LawET!) becomes: \begin{equation} \text{LawET}_{P}\triangleq\forall T.\,(\text{Int}\rightarrow T)\rightarrow\text{FPR}^{T}\times\text{FPR}^{T}\quad.\label{eq:law-expression-tree-type} \end{equation} -How does a given value \inputencoding{latin9}\lstinline!et: LawET!\inputencoding{utf8} -specify a law of a $P$-typeclass (the \textsf{``}\inputencoding{latin9}\lstinline!et!\inputencoding{utf8}-law\textsf{''})? -Suppose we need to verify whether a type $T$ with a $P$-algebra -structure map $p_{T}$ obeys the \inputencoding{latin9}\lstinline!et!\inputencoding{utf8}-law. -First, we apply \inputencoding{latin9}\lstinline!et!\inputencoding{utf8} -to an arbitrary function \inputencoding{latin9}\lstinline!f: Int => T!\inputencoding{utf8} -and obtain a pair \inputencoding{latin9}\lstinline!(lhs, rhs)!\inputencoding{utf8} -of values of type \inputencoding{latin9}\lstinline!FPR[T]!\inputencoding{utf8}:\inputencoding{latin9} +How does a given value \lstinline!et: LawET! specify a law of a $P$-typeclass +(the \textsf{``}\lstinline!et!-law\textsf{''})? Suppose we need to verify whether +a type $T$ with a $P$-algebra structure map $p_{T}$ obeys the \lstinline!et!-law. +First, we apply \lstinline!et! to an arbitrary function \lstinline!f: Int => T! +and obtain a pair \lstinline!(lhs, rhs)! of values of type \lstinline!FPR[T]!: \begin{lstlisting} val f: Int => T = ... val (lhs: FPR[T], rhs: FPR[T]) = et(f) \end{lstlisting} -\inputencoding{utf8}Then we evaluate both expression trees using $T$\textsf{'}s operations (that -is, data from the structure map $p_{T}$):\inputencoding{latin9} +Then we evaluate both expression trees using $T$\textsf{'}s operations (that +is, data from the structure map $p_{T}$): \begin{lstlisting} val (lhsT: T, rhsT: T) = (runner(id)(lhs), runner(id)(rhs)) \end{lstlisting} -\inputencoding{utf8}The \inputencoding{latin9}\lstinline!et!\inputencoding{utf8}-law -is then the condition \inputencoding{latin9}\lstinline!lhsT == rhsT!\inputencoding{utf8}. +The \lstinline!et!-law is then the condition \lstinline!lhsT == rhsT!. -The types \inputencoding{latin9}\lstinline!LawF!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!LawET!\inputencoding{utf8} appear -to be different but turn out to be \emph{equivalent}: +The types \lstinline!LawF! and \lstinline!LawET! appear to be different +but turn out to be \emph{equivalent}: \subsubsection{Statement \label{subsec:Statement-equivalence-law-function-law-expression-tree-P-typeclass}\ref{subsec:Statement-equivalence-law-function-law-expression-tree-P-typeclass}} -The types of fully parametric functions \inputencoding{latin9}\lstinline!LawF!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!LawET!\inputencoding{utf8} (defined -by Eq.~(\ref{eq:P-typeclass-law-type}) and Eq.~(\ref{eq:law-expression-tree-type}) +The types of fully parametric functions \lstinline!LawF! and \lstinline!LawET! +(defined by Eq.~(\ref{eq:P-typeclass-law-type}) and Eq.~(\ref{eq:law-expression-tree-type}) respectively) are equivalent to each other and to the type $\text{FPR}^{\text{Int}}\times\text{FPR}^{\text{Int}}$. \subparagraph{Proof} @@ -3572,17 +3264,14 @@ \subsubsection{Statement \label{subsec:Statement-equivalence-law-function-law-ex \] \vspace{-1\baselineskip} -The types \inputencoding{latin9}\lstinline!LawF!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!LawET!\inputencoding{utf8} are -equivalent because they are both equivalent to the same type. $\square$ +The types \lstinline!LawF! and \lstinline!LawET! are equivalent +because they are both equivalent to the same type. $\square$ -Since the types \inputencoding{latin9}\lstinline!LawF!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!LawET!\inputencoding{utf8}, and -$\text{FPR}^{\text{Int}}\times\text{FPR}^{\text{Int}}$ are equivalent, -we may use either of them according to convenience within a particular -derivation or proof. As an illustration, let us see how a value of -type $\text{FPR}^{\text{Int}}\times\text{FPR}^{\text{Int}}$ gives -rise to a $P$-typeclass law. +Since the types \lstinline!LawF!, \lstinline!LawET!, and $\text{FPR}^{\text{Int}}\times\text{FPR}^{\text{Int}}$ +are equivalent, we may use either of them according to convenience +within a particular derivation or proof. As an illustration, let us +see how a value of type $\text{FPR}^{\text{Int}}\times\text{FPR}^{\text{Int}}$ +gives rise to a $P$-typeclass law. Denote for brevity $\text{LawE}^{T}\triangleq\text{FPR}^{T}\times\text{FPR}^{T}$. Given a value $e:\text{LawE}^{\text{Int}}$ and a $P$-algebra $M$ @@ -3603,8 +3292,7 @@ \subsubsection{Statement \label{subsec:Statement-equivalence-law-function-law-ex type $M$. As an explicit example of a law expression, consider the associativity -law of the \inputencoding{latin9}\lstinline!Monoid!\inputencoding{utf8} -typeclass : +law of the \lstinline!Monoid! typeclass : {*}{*}{*} add an explicit code example with monoid laws{*}{*}{*} @@ -3646,8 +3334,7 @@ \subsubsection{Statement \label{subsec:Statement-P-algebra-morphisms-and-laws}\r By Statement~\ref{subsec:Statement-post-wedge-entails-strong-dinaturality}, the function $l$ is strongly dinatural if the argument type of $l$ can be expressed through a profunctor with the post-wedge property. -The type \inputencoding{latin9}\lstinline!LawF!\inputencoding{utf8} -is: +The type \lstinline!LawF! is: \[ \text{LawF}_{P}=\forall A.\,(P^{A}\rightarrow A)\times(\text{Int}\rightarrow A)\rightarrow A\times A=\forall A.\,R^{A,A}\rightarrow S^{A}\quad, \] @@ -3728,8 +3415,8 @@ \subsubsection{Statement \label{subsec:Statement-P-algebra-morphisms-and-laws}\r The property of \textsf{``}hiding\textsf{''} the law violations justifies the practical use of free typeclass encodings (e.g., the raw tree encoding) that do not satisfy laws. In practice, a free monad program will be interpreted -(or \textsf{``}run\textsf{''}) into a lawful monad such as \inputencoding{latin9}\lstinline!Try!\inputencoding{utf8}, -and the runner will preserve the monad operations. Statement~\ref{subsec:Statement-P-algebra-morphisms-and-laws}(b) +(or \textsf{``}run\textsf{''}) into a lawful monad such as \lstinline!Try!, and the +runner will preserve the monad operations. Statement~\ref{subsec:Statement-P-algebra-morphisms-and-laws}(b) then shows that no law violations will be observable after the runner is applied. We have proved this for our free monad DSL in Section~\ref{subsec:A-first-recipe-monadic-dsl}. Now we see that this is a general property that applies to all $P$-typeclasses. @@ -3741,10 +3428,9 @@ \subsubsection{Statement \label{subsec:Statement-P-algebra-morphisms-and-laws}\r \subsection{Free $P$-typeclasses that satisfy a subset of the laws\label{subsec:Free--typeclasses-that-satisfy-laws}} In Section~\ref{subsec:Free-monoids}, we have seen four different -encodings of the free monoid (denoted by \inputencoding{latin9}\lstinline!F1!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!F2!\inputencoding{utf8}, \inputencoding{latin9}\lstinline!F3!\inputencoding{utf8}, -and \inputencoding{latin9}\lstinline!F4!\inputencoding{utf8}). It -turns out that all those encodings satisfy the requirements of Definition~\ref{subsec:Definition-free-P-typeclass-encoding}, +encodings of the free monoid (denoted by \lstinline!F1!, \lstinline!F2!, +\lstinline!F3!, and \lstinline!F4!). It turns out that all those +encodings satisfy the requirements of Definition~\ref{subsec:Definition-free-P-typeclass-encoding}, although they obey different subsets of the laws of monoids. We will now generalize that situation to $P$-typeclasses and study the properties of free $P$-typeclass constructions that satisfy only a subset of @@ -3753,18 +3439,18 @@ \subsection{Free $P$-typeclasses that satisfy a subset of the laws\label{subsec: free $P$-typeclass encoding as specified by Definition~\ref{subsec:Definition-free-P-typeclass-encoding}. Namely, the next statement shows that a functor $E$ is a free $P$-typeclass -encoding if it is a strict subset of the raw tree encoding (\inputencoding{latin9}\lstinline!FPR!\inputencoding{utf8}) -and has certain additional properties. In this way, we can use \inputencoding{latin9}\lstinline!FPR!\inputencoding{utf8} +encoding if it is a strict subset of the raw tree encoding (\lstinline!FPR!) +and has certain additional properties. In this way, we can use \lstinline!FPR! to validate other free $P$-typeclass encodings with less work than if we were applying Definition~\ref{subsec:Definition-free-P-typeclass-encoding} directly. \subsubsection{Statement \label{subsec:Statement-compatible-retract-of-free-monad}\ref{subsec:Statement-compatible-retract-of-free-monad}} -Let $E$ be a functor such that: +Let $P$ be a traversable functor and let $E$ be a functor such that: 1. For any type $T$, the type $E^{T}$ is a $P$-algebra with a structure -map $p_{E}^{T}:P^{E^{T}}\rightarrow E^{T}$, natural in $T$. +map $p_{E}^{T}:P^{E^{T}}\rightarrow E^{T}$ natural in $T$. 2. There exist natural transformations $\text{pu}_{E}^{T}:T\rightarrow E^{T}$ and $\text{in}_{E}^{T}:E^{T}\rightarrow\text{FPR}^{T}$ (which is @@ -3786,8 +3472,9 @@ \subsubsection{Statement \label{subsec:Statement-compatible-retract-of-free-mona is a $P$-algebra morphism. 5. The $P$-algebra $E^{T}$ satisfies some (or all) of the $P$-typeclass -laws but \emph{no other} laws. To formulate this requirement precisely, -we use law expressions $e:\text{FPR}^{\text{Int}}\times\text{FPR}^{\text{Int}}$ +laws but \emph{no other} laws. {*}{*}{*}This is misleading, as in +fact $E^{T}$ does satisfy more laws for some $T$; say for $T=\bbnum 0$.{*}{*}{*}To +formulate this requirement precisely, we use law expressions $e:\text{FPR}^{\text{Int}}\times\text{FPR}^{\text{Int}}$ for describing $P$-typeclass laws (see Section~\ref{subsec:Describing-laws-of-P-typeclasses-as-values}). We say that $E^{T}$ obeys \textsf{``}no other laws than those of the $P$-typeclass\textsf{''} if for any $e:\text{FPR}^{\text{Int}}\times\text{FPR}^{\text{Int}}$ @@ -3803,10 +3490,10 @@ \subsubsection{Statement \label{subsec:Statement-compatible-retract-of-free-mona \textbf{(a)} The functor $E$ is a free $P$-typeclass encoding. When the function $\text{in}_{E}$ is itself a $P$-algebra morphism then -the encoding $E$ is equivalent to \inputencoding{latin9}\lstinline!FPR!\inputencoding{utf8}. -(This statement does \emph{not} require $\text{in}_{E}^{T}$ to be -a $P$-algebra morphism. Then we are able to describe free $P$-typeclass -encodings $E^{T}$ that are different from $\text{FPR}^{T}$.) +the encoding $E$ is equivalent to \lstinline!FPR!. (This statement +does \emph{not} require $\text{in}_{E}^{T}$ to be a $P$-algebra +morphism. Then we are able to describe free $P$-typeclass encodings +$E^{T}$ that are different from $\text{FPR}^{T}$.) \textbf{(b)} The universal evaluator $\text{eval}_{E}^{C}$ does not depend on the choice of the function $\text{in}_{E}$ as long as the @@ -3887,11 +3574,9 @@ \subsubsection{Statement \label{subsec:Statement-compatible-retract-of-free-mona \begin{align} p_{E^{C}}\bef\text{in}_{E}^{C}\bef\text{eval}_{\text{FPR}}^{C}\overset{?}{=}\big(\text{in}_{E}^{C}\bef\text{eval}_{\text{FPR}}^{C}\big)^{\uparrow P}\bef p_{C}\quad.\label{eq:P-algebra-morphism-law-for-in-r-derivation1} \end{align} - -{*}{*}{*}Verifying Eq.~(\ref{eq:P-algebra-morphism-law-for-in-r-derivation1}) -takes much more work. We first note that for any $C$ satisfying the -given assumptions, the $P$-algebra morphism law already holds for -$\text{eval}_{\text{FPR}}^{C}$: +Verifying Eq.~(\ref{eq:P-algebra-morphism-law-for-in-r-derivation1}) +takes more work. We first note that for any $C$ satisfying the given +assumptions, the $P$-algebra morphism law already holds for $\text{eval}_{\text{FPR}}^{C}$: \[ p_{\text{FPR}^{C}}\bef\text{eval}_{\text{FPR}}^{C}=\big(\text{eval}_{\text{FPR}}^{C}\big)^{\uparrow P}\bef p_{C}\quad. \] @@ -3935,8 +3620,6 @@ \subsubsection{Statement \label{subsec:Statement-compatible-retract-of-free-mona We need to write explicitly a law that holds in $E^{T}$. -first set $T=\text{Int}$ - {*}{*}{*} This concludes the proof that $\text{eval}_{E}^{C}$ is a $P$-algebra @@ -4411,9 +4094,11 @@ \section{Slides } \texttt{\textcolor{blue}{\footnotesize{}def a2p{[}A{]}(a: A): P{[}A{]} = PZ{[}A, A{]}(a, identity)}}{\footnotesize\par} -Cannot extract $Z$ out of $P^{A}$ – the type $Z$ is hidden +Cannot extract $Z$ out of $P^{A}$ \textendash{} the type $Z$ is +hidden -\emph{Can} extract $A$ out of $P^{A}$ – do not need to know $Z$ +\emph{Can} extract $A$ out of $P^{A}$ \textendash{} do not need +to know $Z$ \texttt{\textcolor{blue}{\footnotesize{}def p2a{[}A{]}: P{[}A{]} $\rightarrow$ A = \{ case PZ(z, f) $\rightarrow$ f(z) \}}}{\footnotesize\par} @@ -4565,13 +4250,13 @@ \section{Slides } lawful semigroup) To show equivalence between FSZ and Chz, write code for the type ChZ -and the two directions of the isomorphism,\inputencoding{latin9} +and the two directions of the isomorphism, \begin{lstlisting} trait FSCh[Z] { def run[S](empty: Z => S, combine: (S, S) => S): S } def fsz2ch[Z](fsz: NEList[Z]): FSCh[Z] = ??? def ch2fsz[Z](ch: FSCh[Z]): NEList[Z] = ??? \end{lstlisting} -\inputencoding{utf8} + \paragraph{Church encoding V: Type classes} @@ -4642,7 +4327,7 @@ \section{Slides } Tree encoding, reduced encodings, Church encoding -What are the laws for the{\footnotesize{} $\text{FreeC}^{F,A}$} – +What are the laws for the{\footnotesize{} $\text{FreeC}^{F,A}$} \textendash{} \textsf{``}free instance of $C$ over $F$\textsf{''}? For all $F^{\bullet}$, must have \texttt{\textcolor{blue}{\footnotesize{}wrap{[}A{]}}} @@ -4898,7 +4583,7 @@ \section{Slides } For any $P^{:C},Q^{:C},g^{:Z\rightarrow P}$, and a typeclass-preserving $f^{:P\rightarrow Q}$, we have{\footnotesize{} \[ -\text{run}^{P}(g)\bef f=\text{run}^{Q}\left(g\bef f\right)\quad\quad\text{– \textsf{``}universal property\textsf{''} of }\text{run} +\text{run}^{P}(g)\bef f=\text{run}^{Q}\left(g\bef f\right)\quad\quad\text{\textendash\ \textquotedblleft universal property\textquotedblright\ of }\text{run} \] }{\footnotesize{} \[ @@ -4920,7 +4605,8 @@ \section{Slides } We would like to combine generating constructors $Z_{1}$, $Z_{2}$, etc. -In a monadic DSL – combine different operations defined separately +In a monadic DSL \textendash{} combine different operations defined +separately Note: monads do not compose in general @@ -4999,22 +4685,22 @@ \subsection{Exercises} \subsubsection{Exercise \label{subsec:Exercise-free-monad-example}\ref{subsec:Exercise-free-monad-example}\index{exercises}} The \textbf{interactive input-output}\index{monads!interactive input-output monad} -monad is defined recursively by:\inputencoding{latin9} +monad is defined recursively by: \begin{lstlisting} sealed trait TIO[A] final case class Pure[A](a: A) extends TIO[A] final case class Read[A](read: P => TIO[A]) extends TIO[A] final case class Write[A](output: Q, next: TIO[A]) extends TIO[A] \end{lstlisting} -\inputencoding{utf8}In the type notation, this is written as: +In the type notation, this is written as: \[ \text{TIO}^{A}\triangleq A+(P\rightarrow\text{TIO}^{A})+Q\times\text{TIO}^{A}\quad, \] -Here $P$ and $Q$ are fixed types. The monad \inputencoding{latin9}\lstinline!TIO!\inputencoding{utf8} -represents computations that may consume an input value of type $P$ -or produce an output value of type $Q$. Use the free monad construction -to show that \inputencoding{latin9}\lstinline!TIO!\inputencoding{utf8} -is a lawful monad. Implement a monad instance for \inputencoding{latin9}\lstinline!TIO!\inputencoding{utf8}. +Here $P$ and $Q$ are fixed types. The monad \lstinline!TIO! represents +computations that may consume an input value of type $P$ or produce +an output value of type $Q$. Use the free monad construction to show +that \lstinline!TIO! is a lawful monad. Implement a monad instance +for \lstinline!TIO!. \subsubsection{Exercise \label{subsec:Exercise-free-type-1}\ref{subsec:Exercise-free-type-1}} @@ -5036,24 +4722,22 @@ \subsubsection{Exercise \label{subsec:Exercise-free-type-2}\ref{subsec:Exercise- \subsubsection{Exercise \label{subsec:Exercise-free-type-3}\ref{subsec:Exercise-free-type-3}} -\textbf{(a)} Implement a monadic DSL with operations \inputencoding{latin9}\lstinline!put: A => Unit!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!get: Unit => A!\inputencoding{utf8}. -These operations should store and retrieve a state value of type \inputencoding{latin9}\lstinline!A!\inputencoding{utf8}. -Test on some example programs written in that DSL. - -\textbf{(b)} Implement a monadic DSL with operations \inputencoding{latin9}\lstinline!put: A => Unit!\inputencoding{utf8}, -\inputencoding{latin9}\lstinline!get: Unit => Option[A]!\inputencoding{utf8}, -and \inputencoding{latin9}\lstinline!clear: Unit => Unit!\inputencoding{utf8}. -These operations should store and retrieve a state value of type \inputencoding{latin9}\lstinline!A!\inputencoding{utf8}. -Running \inputencoding{latin9}\lstinline!clear!\inputencoding{utf8} -should delete the state value. When there is no state value, \inputencoding{latin9}\lstinline!get!\inputencoding{utf8} -should return \inputencoding{latin9}\lstinline!None!\inputencoding{utf8}. +\textbf{(a)} Implement a monadic DSL with operations \lstinline!put: A => Unit! +and \lstinline!get: Unit => A!. These operations should store and +retrieve a state value of type \lstinline!A!. Test on some example +programs written in that DSL. + +\textbf{(b)} Implement a monadic DSL with operations \lstinline!put: A => Unit!, +\lstinline!get: Unit => Option[A]!, and \lstinline!clear: Unit => Unit!. +These operations should store and retrieve a state value of type \lstinline!A!. +Running \lstinline!clear! should delete the state value. When there +is no state value, \lstinline!get! should return \lstinline!None!. Test on some example programs. \subsubsection{Exercise \label{subsec:Exercise-free-type-4}\ref{subsec:Exercise-free-type-4}} Implement the Church encoding of the type constructor $P^{A}\triangleq\text{Int}+A\times A$. -For the resulting type constructor, implement a \inputencoding{latin9}\lstinline!Functor!\inputencoding{utf8} +For the resulting type constructor, implement a \lstinline!Functor! instance. \subsubsection{Exercise \label{subsec:Exercise-free-type-5}\ref{subsec:Exercise-free-type-5}} @@ -5066,12 +4750,10 @@ \subsubsection{Exercise \label{subsec:Exercise-free-type-5}\ref{subsec:Exercise- \subsubsection{Exercise \label{subsec:Exercise-free-type-6}\ref{subsec:Exercise-free-type-6}} Assuming that $F^{\bullet}$ is a functor, define $Q^{A}\triangleq\exists Z.F^{Z}\times\left(Z\rightarrow A\right)$ -and implement \inputencoding{latin9}\lstinline!f2q!\inputencoding{utf8}$:F^{A}\rightarrow Q^{A}$ -and \inputencoding{latin9}\lstinline!q2f!\inputencoding{utf8}$:Q^{A}\rightarrow F^{A}$. +and implement \lstinline!f2q!$:F^{A}\rightarrow Q^{A}$ and \lstinline!q2f!$:Q^{A}\rightarrow F^{A}$. Show that these functions are natural transformations, and that they are inverses of each other \textsf{``}observationally\textsf{''}, i.e., after applying -\inputencoding{latin9}\lstinline!q2f!\inputencoding{utf8} in order -to compare values of $Q^{A}$. +\lstinline!q2f! in order to compare values of $Q^{A}$. \subsubsection{Exercise \label{subsec:Exercise-free-type-7}\ref{subsec:Exercise-free-type-7}} @@ -5356,18 +5038,17 @@ \section{Working with quantified types} the quantified type $\forall X.\,F^{X}$ and the type expression $F^{X}$ that contains the type parameter $X$. In both cases, $X$ is a completely unknown type parameter, and an example of Scala code implementing -those types would be:\inputencoding{latin9} +those types would be: \begin{lstlisting} def f[X]: F[X] = ??? \end{lstlisting} -\inputencoding{utf8}However, the type quantifier $\forall X$ implies that all values +However, the type quantifier $\forall X$ implies that all values of type $\forall X.\,F^{X}$ must be implemented via fully parametric -code. The code of the function \inputencoding{latin9}\lstinline!f[X]!\inputencoding{utf8} -may not make decisions based on the actual type passed at run time -as the type parameter \inputencoding{latin9}\lstinline!X!\inputencoding{utf8} -into the function. The assumption of full parametricity enables us -to reason about quantified types in a special way. This section explores -the techniques of this reasoning. +code. The code of the function \lstinline!f[X]! may not make decisions +based on the actual type passed at run time as the type parameter +\lstinline!X! into the function. The assumption of full parametricity +enables us to reason about quantified types in a special way. This +section explores the techniques of this reasoning. \subsection{The Yoneda identities for type constructors} @@ -5405,8 +5086,8 @@ \subsubsection{Statement \label{subsec:Statement-covariant-yoneda-identity-for-t brevity, we will write just $F$ and $P$ instead of $F^{\bullet}$ and $P^{\bullet}$. -The isomorphism is implemented via two functions \inputencoding{latin9}\lstinline!toC!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!fromC!\inputencoding{utf8}, +The isomorphism is implemented via two functions \lstinline!toC! +and \lstinline!fromC!, \begin{align*} \text{toC}:S^{P}\rightarrow\forall F.\,(P\leadsto F)\rightarrow S^{F}\quad, & \quad\quad\text{toC}\triangleq s^{:S^{P}}\rightarrow\forall F.\,g^{:P\leadsto F}\rightarrow s\triangleright g^{\uparrow S}\quad,\\ \text{fromC}:(\forall F.\,(P\leadsto F)\rightarrow S^{F})\rightarrow S^{P}\quad, & \quad\quad\text{fromC}\triangleq\sigma^{:\forall F.\,(P\leadsto F)\rightarrow S^{F}}\rightarrow\sigma^{P}(\text{id}^{:P\leadsto P})\quad. @@ -5438,8 +5119,8 @@ \subsubsection{Statement \label{subsec:Statement-covariant-yoneda-identity-for-t & =(g^{:P\leadsto P}\rightarrow s\triangleright g^{\uparrow S})(\text{id}^{:P\leadsto P})=s\triangleright\text{id}^{\uparrow S}=s\quad. \end{align*} It remains to check that the function $\sigma^{F}\triangleq g^{:P\leadsto F}\rightarrow s\triangleright g^{\uparrow S}$, -used as an argument of \inputencoding{latin9}\lstinline!fromC!\inputencoding{utf8}, -is natural in $F$. To verify the naturality law~(\ref{eq:assumed-naturality-of-argument-sigma}): +used as an argument of \lstinline!fromC!, is natural in $F$. To +verify the naturality law~(\ref{eq:assumed-naturality-of-argument-sigma}): \begin{align*} {\color{greenunder}\text{expect to equal }\sigma^{R}(f\bef g):}\quad & \sigma^{Q}(f)\bef g^{\uparrow S}=s\triangleright f^{\uparrow S}\bef g^{\uparrow S}=s\triangleright(f\bef g)^{\uparrow S}=\sigma^{R}(f\bef g)\quad. \end{align*} @@ -5468,8 +5149,8 @@ \subsection{Recursive type equations with different fixpoints} \begin{equation} L^{A}\cong\bbnum 1+(\bbnum 1\rightarrow A\times L^{A})\quad.\label{eq:fixpoint-type-equation-for-oncall-list} \end{equation} -We may also write the same equation using a suitable structure functor -$F$ like this: +We may write the same equation using a recursion scheme $F$ like +this: \[ L^{A}\cong F^{A,L^{A}}\quad,\quad\quad F^{A,R}\triangleq\bbnum 1+(\bbnum 1\rightarrow A\times R)\quad. \] @@ -5489,9 +5170,9 @@ \subsection{Recursive type equations with different fixpoints} equation $L^{A}\cong F^{A,L^{A}}$, {*}{*}{*} To see that the fixpoint equation~(\ref{eq:fixpoint-type-equation-for-oncall-list}) -has (at least) three inequivalent solutions, consider a function \inputencoding{latin9}\lstinline!toList!\inputencoding{utf8} -that converts a value of type $L^{A}$ into a sequence of type \inputencoding{latin9}\lstinline!List[A]!\inputencoding{utf8}, -whose elements are eagerly evaluated. The function \inputencoding{latin9}\lstinline!toList!\inputencoding{utf8} +has (at least) three inequivalent solutions, consider a function \lstinline!toList! +that converts a value of type $L^{A}$ into a sequence of type \lstinline!List[A]!, +whose elements are eagerly evaluated. The function \lstinline!toList! keeps recursively requesting new elements of the on-call list and accumulates the resulting values: \[ @@ -5501,12 +5182,11 @@ \subsection{Recursive type equations with different fixpoints} \bbnum 1\rightarrow A\times L^{A} & \bbnum 0 & p\rightarrow p(1)\triangleright(\text{id}\boxtimes\overline{\text{toList}}) \end{array}\quad. \] -Does this function terminate? It is clear that \inputencoding{latin9}\lstinline!toList!\inputencoding{utf8} +Does this function terminate? It is clear that \lstinline!toList! will terminate only if the on-call list eventually stops yielding new values of type $A$. On the other hand, if the on-call list never -stops yielding new values of type $A$, the function \inputencoding{latin9}\lstinline!toList!\inputencoding{utf8} -will not terminate. So, \inputencoding{latin9}\lstinline!toList!\inputencoding{utf8} -must be a partial function. +stops yielding new values of type $A$, the function \lstinline!toList! +will not terminate. So, \lstinline!toList! must be a partial function. Let us denote by $L_{\text{fin}}^{A}$ the subtype of $L^{A}$ consisting of finite lists, i.e., on-call lists that eventually stop yielding @@ -5521,24 +5201,22 @@ \subsection{Recursive type equations with different fixpoints} Let us also denote by $L_{\text{inf}}^{A}$ the subtype corresponding to \textsf{``}always infinite\textsf{''} on-call lists, i.e., those that \emph{never} -stop yielding new values of type $A$. Then the function \inputencoding{latin9}\lstinline!toList!\inputencoding{utf8} +stop yielding new values of type $A$. Then the function \lstinline!toList! does not terminate for any argument of type $L_{\text{inf}}^{A}$. So, the types $L_{\text{fin}}^{A}$ and $L_{\text{inf}}^{A}$ are \emph{not} equivalent. Were they equivalent, we would have an isomorphism $q:L_{\text{inf}}^{A}\rightarrow L_{\text{fin}}^{A}$, and then we -could compose $q$ with \inputencoding{latin9}\lstinline!toList!\inputencoding{utf8} -to obtain a function $\text{toList}:L_{\text{inf}}^{A}\rightarrow\text{List}^{A}$ +could compose $q$ with \lstinline!toList! to obtain a function $\text{toList}:L_{\text{inf}}^{A}\rightarrow\text{List}^{A}$ that terminates, which is impossible. To show that both types ($L_{\text{fin}}^{A}$ and $L_{\text{inf}}^{A}$) satisfy the fixpoint type equation~(\ref{eq:fixpoint-type-equation-for-oncall-list}), -we can implement the corresponding isomorphisms \inputencoding{latin9}\lstinline!fix!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!unfix!\inputencoding{utf8}. -Each of these functions will either add or remove one element at the -beginning of the list. These operations keep finite lists finite and -infinite lists infinite. So, a composition (such as, $\text{unfix}\bef\text{fix}$) -of these isomorphisms will act as an identity function on $L_{\text{fin}}^{A}$ -or on $L_{\text{inf}}^{A}$. +we can implement the corresponding isomorphisms \lstinline!fix! and +\lstinline!unfix!. Each of these functions will either add or remove +one element at the beginning of the list. These operations keep finite +lists finite and infinite lists infinite. So, a composition (such +as, $\text{unfix}\bef\text{fix}$) of these isomorphisms will act +as an identity function on $L_{\text{fin}}^{A}$ or on $L_{\text{inf}}^{A}$. The type $L^{A}$ is an on-call list that may or may not terminate. So, $L^{A}$ is equivalent to a disjunction $L_{\text{fin}}^{A}+L_{\text{inf}}^{A}$. @@ -5621,11 +5299,11 @@ \subsection{The Church encoding of recursive types\label{subsec:The-Church-encod T\cong\forall X.\,(F^{X}\rightarrow X)\rightarrow X\quad\text{if the type }T\text{ is defined by }T\triangleq F^{T}\quad.\label{eq:Church-encoding-recursive-type} \end{equation} The Scala code for the type $\forall X.\,(F^{X}\rightarrow X)\rightarrow X$ -is:\inputencoding{latin9} +is: \begin{lstlisting} trait TC[F[_]] { def run[X](fold: F[X] => X): X } \end{lstlisting} -\inputencoding{utf8}In this section, we will study the type equivalence~(\ref{eq:Church-encoding-recursive-type}). +In this section, we will study the type equivalence~(\ref{eq:Church-encoding-recursive-type}). Note that the Yoneda lemma cannot be used to prove Eq.~(\ref{eq:Church-encoding-recursive-type}). The Yoneda lemma only applies to types of the form $\forall X.\,(A\rightarrow X)\rightarrow F^{X}$, @@ -5660,8 +5338,8 @@ \subsubsection{Statement \label{subsec:Statement-Church-encoding-recursive-type- \[ \text{fix}:F^{T}\rightarrow T\quad,\quad\quad\text{unfix}:T\rightarrow F^{T}\quad. \] -Begin by writing out the type signature of \inputencoding{latin9}\lstinline!fix!\inputencoding{utf8} -and an implementation with a typed hole: +Begin by writing out the type signature of \lstinline!fix! and an +implementation with a typed hole: \begin{align*} & \text{fix}:F^{\forall X.\,(F^{X}\rightarrow X)\rightarrow X}\rightarrow\forall Y.\,(F^{Y}\rightarrow Y)\rightarrow Y\quad,\\ & \text{fix}\triangleq f^{:F^{\forall X.\,(F^{X}\rightarrow X)\rightarrow X}}\rightarrow\forall Y.\,q^{:F^{Y}\rightarrow Y}\rightarrow\text{???}^{:Y}\quad. @@ -5672,7 +5350,7 @@ \subsubsection{Statement \label{subsec:Statement-Church-encoding-recursive-type- \text{fix}=f^{:F^{\forall X.\,(F^{X}\rightarrow X)\rightarrow X}}\rightarrow\forall Y.\,q^{:F^{Y}\rightarrow Y}\rightarrow\big(\text{???}^{:F^{Y}}\big)\triangleright q\quad. \] The functor $F$ is arbitrary, so the only way of computing a value -of type $F^{Y}$ is to use the given value $f$ with a \inputencoding{latin9}\lstinline!map!\inputencoding{utf8} +of type $F^{Y}$ is to use the given value $f$ with a \lstinline!map! method (the only method we can use with any functor $F$): \[ \text{???}^{:F^{Y}}=f\triangleright(p^{:\forall X.\,(F^{X}\rightarrow X)\rightarrow X}\rightarrow\text{???}^{:Y})^{\uparrow F}\quad. @@ -5683,12 +5361,12 @@ \subsubsection{Statement \label{subsec:Statement-Church-encoding-recursive-type- \[ \text{???}^{:F^{Y}}=f\triangleright(p^{:\forall X.\,(F^{X}\rightarrow X)\rightarrow X}\rightarrow q\triangleright p^{Y})^{\uparrow F}\quad. \] -This allows us to complete the code of \inputencoding{latin9}\lstinline!fix!\inputencoding{utf8}: +This allows us to complete the code of \lstinline!fix!: \[ \text{fix}\triangleq f^{:F^{\forall X.\,(F^{X}\rightarrow X)\rightarrow X}}\rightarrow\forall Y.\,q^{:F^{Y}\rightarrow Y}\rightarrow f\triangleright\big(p^{:\forall X.\,(F^{X}\rightarrow X)\rightarrow X}\rightarrow q\triangleright p^{Y}\big)^{\uparrow F}\triangleright q\quad. \] -We turn to implementing \inputencoding{latin9}\lstinline!unfix!\inputencoding{utf8}: +We turn to implementing \lstinline!unfix!: \begin{align*} & \text{unfix}:\big(\forall Y.\,(F^{Y}\rightarrow Y)\rightarrow Y\big)\rightarrow F^{\forall X.\,(F^{X}\rightarrow X)\rightarrow X\big)}\quad,\\ & \text{unfix}\triangleq t^{:\forall Y.\,(F^{Y}\rightarrow Y)\rightarrow Y}\rightarrow\text{???}^{:F^{T}}\quad. @@ -5699,14 +5377,12 @@ \subsubsection{Statement \label{subsec:Statement-Church-encoding-recursive-type- \text{unfix}=t^{:\forall Y.\,(F^{Y}\rightarrow Y)\rightarrow Y}\rightarrow t^{F^{T}}\big(\text{???}^{:F^{F^{T}}\rightarrow F^{T}}\big)\quad. \] We can now fill the typed hole $\text{???}^{:F^{F^{T}}\rightarrow F^{T}}$ -by lifting \inputencoding{latin9}\lstinline!fix!\inputencoding{utf8} -to the functor $F$: +by lifting \lstinline!fix! to the functor $F$: \[ \text{unfix}\triangleq t^{:\forall Y.\,(F^{Y}\rightarrow Y)\rightarrow Y}\rightarrow t^{F^{T}}\big(\text{fix}^{\uparrow F}\big)\quad. \] -It remains to show that \inputencoding{latin9}\lstinline!fix!\inputencoding{utf8} -and \inputencoding{latin9}\lstinline!unfix!\inputencoding{utf8} are +It remains to show that \lstinline!fix! and \lstinline!unfix! are inverses. Start with one direction: \begin{align*} {\color{greenunder}\text{expect to equal }t:}\quad & t^{:T}\triangleright\text{unfix}\bef\text{fix}=t^{F^{T}}(\text{fix}^{\uparrow F})\triangleright\text{fix}\\ @@ -5803,14 +5479,14 @@ \subsubsection{Statement \label{subsec:Statement-Church-encoding-recursive-type- A curious property of the type $T$ is that it is a function with argument of type $F^{X}\rightarrow X$, where $X$ can be any type, including $T$ itself. But we already have a function of type $F^{T}\rightarrow T$; -it is the function \inputencoding{latin9}\lstinline!fix!\inputencoding{utf8}. -So, applying a value $t$ of type $T$ to the function \inputencoding{latin9}\lstinline!fix!\inputencoding{utf8} -yields again a value of type $T$. As the next statement shows, that -value is the same as $t$: +it is the function \lstinline!fix!. So, applying a value $t$ of +type $T$ to the function \lstinline!fix! yields again a value of +type $T$. As the next statement shows, that value is the same as +$t$: \subsubsection{Statement \label{subsec:Statement-strong-dinaturality-property-of-fix}\ref{subsec:Statement-strong-dinaturality-property-of-fix}} -Consider the Church-encoded type $T$ and the function \inputencoding{latin9}\lstinline!fix!\inputencoding{utf8} +Consider the Church-encoded type $T$ and the function \lstinline!fix! defined in Statement~\ref{subsec:Statement-Church-encoding-recursive-type-covariant}. It follows from the strong dinaturality law\footnote{\index{Dan Doel}Dan Doel gave a proof using relational parametricity: see \texttt{\href{https://cs.stackexchange.com/questions/131901/}{https://cs.stackexchange.com/questions/131901/}}} for any value $t^{:T}$ that: @@ -5868,11 +5544,11 @@ \subsubsection{Statement \label{subsec:Statement-strong-dinaturality-property-of \subsubsection{Statement \label{subsec:Statement-catamorphism-church-encoding}\ref{subsec:Statement-catamorphism-church-encoding}} -\textbf{(a)} The function \inputencoding{latin9}\lstinline!cata!\inputencoding{utf8} -(defined above) is a fixpoint-preserving function. +\textbf{(a)} The function \lstinline!cata! (defined above) is a fixpoint-preserving +function. \textbf{(b)} Any other fixpoint-preserving function of type $T\rightarrow R$ -is equal to \inputencoding{latin9}\lstinline!cata!\inputencoding{utf8}. +is equal to \lstinline!cata!. \subparagraph{Proof} @@ -5918,23 +5594,19 @@ \subsubsection{Statement \label{subsec:Statement-co-Yoneda-two-identities}\ref{s \end{align*} $\square$ -The Scala type \inputencoding{latin9}\lstinline!Any!\inputencoding{utf8} -closely corresponds to the type $\exists X.\,X$, which is observationally -equivalent to \inputencoding{latin9}\lstinline!Unit!\inputencoding{utf8}. -The advantage of using \inputencoding{latin9}\lstinline!Any!\inputencoding{utf8} -instead of \inputencoding{latin9}\lstinline!Unit!\inputencoding{utf8} -is that \inputencoding{latin9}\lstinline!Any!\inputencoding{utf8} +The Scala type \lstinline!Any! closely corresponds to the type $\exists X.\,X$, +which is observationally equivalent to \lstinline!Unit!. The advantage +of using \lstinline!Any! instead of \lstinline!Unit! is that \lstinline!Any! is understood by the Scala compiler as a supertype of \emph{all} Scala -types (while \inputencoding{latin9}\lstinline!Unit!\inputencoding{utf8} -is not). Indeed, for any type $T$ there is an injective function -$T\rightarrow\exists X.\,X$, This function corresponds to a function -of type \inputencoding{latin9}\lstinline!T => Any!\inputencoding{utf8} -in Scala:\inputencoding{latin9} +types (while \lstinline!Unit! is not). Indeed, for any type $T$ +there is an injective function $T\rightarrow\exists X.\,X$, This +function corresponds to a function of type \lstinline!T => Any! in +Scala: \begin{lstlisting} def toAny[T](t: T): Any = t \end{lstlisting} -\inputencoding{utf8} This is just an identity function that relabels the types; so, this -function establishes the subtyping relation \inputencoding{latin9}\lstinline!T <: Any!\inputencoding{utf8}. + This is just an identity function that relabels the types; so, this +function establishes the subtyping relation \lstinline!T <: Any!. \subsection{Exercises\index{exercises}} @@ -5943,10 +5615,10 @@ \subsubsection{Exercise \label{subsec:Exercise-Yoneda}\ref{subsec:Exercise-Yoned Use a Yoneda identity to prove that there are no fully parametric functions with this type: -\inputencoding{latin9}\begin{lstlisting} +\begin{lstlisting} def f[A]: Option[A] => A \end{lstlisting} -\inputencoding{utf8} + \section{Discussion} @@ -6037,41 +5709,42 @@ \subsection{Universally quantified function types cover all other types} \paragraph{Free constructions in mathematics: Example I} -Consider the Cyrillic letter \foreignlanguage{russian}{ц} (ts\`{e}) -and the Chinese word \shui~(shu\textipa{\v i}) +Consider the Cyrillic letter \foreignlanguage{russian}{\textcyr{\cyrc}} +(ts\`{e}) and the Chinese word \shui~(shu\textipa{\v i}) -We want to \emph{multiply} \foreignlanguage{russian}{ц} by \shui. -Multiply how? +We want to \emph{multiply} \foreignlanguage{russian}{\textcyr{\cyrc}} +by \shui. Multiply how? Say, we want an associative (but noncommutative) product of them -So we want to define a \emph{semigroup} that \emph{contains} \foreignlanguage{russian}{ц} +So we want to define a \emph{semigroup} that \emph{contains} \foreignlanguage{russian}{\textcyr{\cyrc}} and \shui~as elements -while we still know nothing about \foreignlanguage{russian}{ц} and -\shui +while we still know nothing about \foreignlanguage{russian}{\textcyr{\cyrc}} +and \shui -Consider the set of all \emph{unevaluated expressions} such as \foreignlanguage{russian}{ц}$\cdot$\shui$\cdot$\shui$\cdot$\foreignlanguage{russian}{ц}$\cdot$\shui +Consider the set of all \emph{unevaluated expressions} such as \foreignlanguage{russian}{\textcyr{\cyrc}}$\cdot$\shui$\cdot$\shui$\cdot$\foreignlanguage{russian}{\textcyr{\cyrc}}$\cdot$\shui -Here \foreignlanguage{russian}{ц}$\cdot$\shui~is different from -\shui$\cdot$\foreignlanguage{russian}{ц} but $\left(a\cdot b\right)\cdot c=a\cdot\left(b\cdot c\right)$ +Here \foreignlanguage{russian}{\textcyr{\cyrc}}$\cdot$\shui~is +different from \shui$\cdot$\foreignlanguage{russian}{\textcyr{\cyrc}} +but $\left(a\cdot b\right)\cdot c=a\cdot\left(b\cdot c\right)$ All these expressions form a \textbf{free semigroup} generated by -\foreignlanguage{russian}{ц} and \shui +\foreignlanguage{russian}{\textcyr{\cyrc}} and \shui -This is the most unrestricted semigroup that contains \foreignlanguage{russian}{ц} +This is the most unrestricted semigroup that contains \foreignlanguage{russian}{\textcyr{\cyrc}} and \shui -Example calculation: (\shui$\cdot$\shui)$\cdot$(\foreignlanguage{russian}{ц}$\cdot$\shui)$\cdot$\foreignlanguage{russian}{ц} -$=$ \shui$\cdot$\shui$\cdot$\foreignlanguage{russian}{ц}$\cdot$\shui$\cdot$\foreignlanguage{russian}{ц} +Example calculation: (\shui$\cdot$\shui)$\cdot$(\foreignlanguage{russian}{\textcyr{\cyrc}}$\cdot$\shui)$\cdot$\foreignlanguage{russian}{\textcyr{\cyrc}} +$=$ \shui$\cdot$\shui$\cdot$\foreignlanguage{russian}{\textcyr{\cyrc}}$\cdot$\shui$\cdot$\foreignlanguage{russian}{\textcyr{\cyrc}} How to represent this as a data type: -\textbf{Tree encoding}: the full expression tree: (((\shui,\shui),(\foreignlanguage{russian}{ц},\shui)),\foreignlanguage{russian}{ц}) +\textbf{Tree encoding}: the full expression tree: (((\shui,\shui),(\foreignlanguage{russian}{\textcyr{\cyrc}},\shui)),\foreignlanguage{russian}{\textcyr{\cyrc}}) Implement the operation $a\cdot b$ as pair constructor (easy) -\textbf{Reduced encoding}, as a \textsf{``}smart\textsf{''} structure: List(\shui,\shui,\foreignlanguage{russian}{ц},\shui,\foreignlanguage{russian}{ц}) +\textbf{Reduced encoding}, as a \textsf{``}smart\textsf{''} structure: List(\shui,\shui,\foreignlanguage{russian}{\textcyr{\cyrc}},\shui,\foreignlanguage{russian}{\textcyr{\cyrc}}) Implement $a\cdot b$ by concatenating the lists (more expensive) @@ -6106,7 +5779,7 @@ \subsection{Universally quantified function types cover all other types} Reduced encoding requires proofs and more complex operations -\subsection{Beyond Yoneda: using parametricity to simplify quantified types} +\subsection{Beyond Yoneda: simplifying quantified types} The covariant Yoneda identity, \[ @@ -6118,10 +5791,9 @@ \subsection{Beyond Yoneda: using parametricity to simplify quantified types} \subsubsection{Statement \label{subsec:Statement-quantifier-across-functor}\ref{subsec:Statement-quantifier-across-functor}} -For any exponential-polynomial functor $F$ and for any profunctor -$P^{X,Y}$, the types $\forall A.\,F^{P^{A,A}}$ and $F^{\forall A.\,P^{A,A}}$ -are equivalent when restricted to fully parametric implementations. -The same holds when $F$ is an exponential-polynomial contrafunctor. +For any polynomial functor $F$ and for any profunctor $P^{X,Y}$, +the types $\forall A.\,F^{P^{A,A}}$ and $F^{\forall A.\,P^{A,A}}$ +are equivalent when restricted to fully parametric implementations. \subparagraph{Proof} @@ -6132,6 +5804,8 @@ \subsubsection{Statement \label{subsec:Statement-existential-quandifier-via-Chur \textbf{(a)} For any profunctor $P^{X,Y}$, the types $\exists A.\,P^{A,A}$ and $\forall B.\,(\forall A.\,(P^{A,A}\rightarrow B))\rightarrow B$ are equivalent when restricted to fully parametric implementations. +{*}{*}{*} I already talked about this earlier in this chapter. Move +proof here? \textbf{(b)} Without the outer quantifier ($\forall B$), this property does not hold: the types $(\exists A.\,P^{A,A})\rightarrow B$ and @@ -6350,7 +6024,7 @@ \subsubsection{Statement \label{subsec:Statement-existential-quandifier-via-Chur the string so in this way we can achieve type safety so the program remains mostly the same except for the type the interpreter remains mostly the same except now it has type safety let\textsf{'}s see how that works -now so let\textsf{'}s implement instead of niño Java file types let\textsf{'}s just +now so let\textsf{'}s implement instead of nio Java file types let\textsf{'}s just have a mock type that represents a file path so now how do we implement run you know it\textsf{'}s the same except now it\textsf{'}s impossible to have pass in a program of type string so the program of type strain can only diff --git a/sofp-src/sofp-functors.lyx b/sofp-src/sofp-functors.lyx index 94ac3a0ec..db195ce07 100644 --- a/sofp-src/sofp-functors.lyx +++ b/sofp-src/sofp-functors.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -1055,10 +1070,7 @@ Option[B] value that wraps transformed data. We will now use this example to develop intuition about manipulating data in a wrapper. -\end_layout - -\begin_layout Standard -Two possible implementations of + Two possible implementations of \begin_inset listings inline true status open @@ -1070,22 +1082,7 @@ map \end_inset - fit the type signature: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "58.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -70baselineskip% -\end_inset - - + will fit the type signature: \begin_inset listings inline false status open @@ -1126,20 +1123,6 @@ def mapY[A, B](oa: Option[A])(f: A => B): Option[B] = \end_inset - -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The code of \begin_inset listings inline true @@ -1279,21 +1262,6 @@ mapY \end_inset and compute: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "58col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1325,20 +1293,6 @@ mapY[A, A](x: Option[A])(identity[A]: A => A): Option[A] \end_inset - -\begin_inset VSpace 10baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The result is always equal to \begin_inset listings inline true @@ -1353,10 +1307,6 @@ x . We can write that fact as an equation: -\begin_inset VSpace -30baselineskip% -\end_inset - - \begin_inset Formula \[ \forall x^{:\text{Opt}^{A}}.\,\,\text{map}\,(x)(\text{id})=x\quad. @@ -1364,15 +1314,6 @@ x \end_inset - -\begin_inset VSpace -85baselineskip% -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent This equation is called the \series bold identity law @@ -1515,10 +1456,6 @@ map \end_inset is: -\begin_inset VSpace -40baselineskip% -\end_inset - - \begin_inset Formula \[ \text{map}^{A,B}\triangleq p^{:\bbnum 1+A}\rightarrow f^{:A\rightarrow B}\rightarrow p\triangleright\,\begin{array}{|c||cc|} @@ -1605,21 +1542,6 @@ map \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "55col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -0baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1647,26 +1569,6 @@ def fmap[A, B](f: A => B): Option[A] => Option[B] = { \end_inset -\begin_inset VSpace -200baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -70baselineskip% -\end_inset - - \begin_inset Formula \begin{equation} \text{fmap}\,(f^{:A\rightarrow B})\triangleq\,\begin{array}{|c||cc|} @@ -1679,10 +1581,6 @@ A & \bbnum 0 & f \end_inset -\begin_inset VSpace -50baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -1992,21 +1890,6 @@ This result is the same as when applying a lifted function \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "30col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2023,20 +1906,6 @@ res2: Option[Int] = Some(2) \end_inset - -\begin_inset VSpace -120baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent It would be confusing and counter-intuitive if \begin_inset listings inline true @@ -2375,21 +2244,6 @@ Now we consider the left-hand side of the law, \end_inset , and write the Scala expressions: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "33col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2461,20 +2315,6 @@ oa.map(f).map(g) == (oa match { \end_inset - -\begin_inset VSpace -500baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent We find that the two sides of the law have identical code. \end_layout @@ -2507,10 +2347,6 @@ fmap \end_inset and omit the types: -\begin_inset VSpace -45baselineskip% -\end_inset - - \begin_inset Formula \begin{align*} & \text{fmap}\,(f)\bef\text{fmap}\,(g)=\,\begin{array}{||cc|} @@ -2533,14 +2369,9 @@ fmap \end_inset -\begin_inset VSpace -140baselineskip% -\end_inset - - \end_layout \begin_layout Standard -\noindent These calculations prove that the \begin_inset listings inline true @@ -2856,8 +2687,8 @@ fmap must satisfy: \begin_inset Formula \begin{align} -\text{identity law of }L:\quad & \text{fmap}_{L}(\text{id}^{:A\rightarrow A})=\text{id}^{:L^{A}\rightarrow L^{A}}\quad,\label{eq:f-identity-law-functor-fmap}\\ -\text{composition law of }L:\quad & \text{fmap}_{L}(f^{:A\rightarrow B}\bef g^{:B\rightarrow C})=\text{fmap}_{L}(f^{:A\rightarrow B})\bef\text{fmap}_{L}(g^{:B\rightarrow C})\quad.\label{eq:f-composition-law-functor-fmap} +\text{identity law}:\quad & \text{fmap}_{L}(\text{id}^{:A\rightarrow A})=\text{id}^{:L^{A}\rightarrow L^{A}}\quad,\label{eq:f-identity-law-functor-fmap}\\ +\text{composition law}:\quad & \text{fmap}_{L}(f^{:A\rightarrow B}\bef g^{:B\rightarrow C})=\text{fmap}_{L}(f^{:A\rightarrow B})\bef\text{fmap}_{L}(g^{:B\rightarrow C})\quad.\label{eq:f-composition-law-functor-fmap} \end{align} \end_inset @@ -2957,19 +2788,7 @@ noprefix "false" \end_inset -) is shown -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -190baselineskip% -\end_inset - - +) is: \begin_inset Formula \[ \xymatrix{\xyScaleY{1.5pc}\xyScaleX{3pc} & L^{B}\ar[rd]\sp(0.6){~~\text{fmap}_{L}(g^{:B\rightarrow C})}\\ @@ -2979,20 +2798,7 @@ L^{A}\ar[ru]\sp(0.4){\text{fmap}_{L}(f^{:A\rightarrow B})\ ~}\ar[rr]\sb(0.5){\te \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -200baselineskip% -\end_inset - - -\end_layout - -\end_inset - -at left. - There are two paths from +There are two paths from \begin_inset Formula $L^{A}$ \end_inset @@ -3774,7 +3580,7 @@ noprefix "false" status collapsed \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -5484,8 +5290,8 @@ status open \end_layout \begin_layout Standard -Turning now to the code notation, we begin by writing the definition of - the type constructor +We will now write the same proof in the code notation. + Begin with the definition of the type constructor \begin_inset Formula $\text{List}^{A}$ \end_inset @@ -6532,19 +6338,7 @@ yield \end_inset - syntax is that, at first sight, functor blocks (such as the code -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "36col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - + syntax is that, at first sight, functor blocks (such as this code: \begin_inset listings inline false status open @@ -6557,34 +6351,26 @@ for { x <- p; ... \end_inset - -\end_layout +appear to compute the value +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - +expr(x) \end_layout \end_inset - shown at left) appear to compute, or to -\begin_inset Quotes eld -\end_inset - -yield -\begin_inset Quotes erd -\end_inset - -, the value + because the code says \begin_inset listings inline true status open \begin_layout Plain Layout -expr(x) +yield expr(x) \end_layout \end_inset @@ -6756,21 +6542,6 @@ Option \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "47col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -6826,23 +6597,6 @@ res1: Option[String] = Some(Have 12) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -120baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In this code, the \begin_inset listings inline true @@ -8716,7 +8470,7 @@ final case class H[A](r: A => Int) \begin_inset Formula \[ -\quad\quad\quad\quad\quad\quad\quad\quad H^{A}\triangleq A\rightarrow\text{Int}\quad. +\quad\quad\quad H^{A}\triangleq A\rightarrow\text{Int}\quad. \] \end_inset @@ -8913,7 +8667,11 @@ generalized algebraic data types \series bold generalized algebraic data types \series default - (GADTs + ( +\series bold +GADT +\series default +s \begin_inset Index idx status open @@ -8924,24 +8682,39 @@ GADT \end_inset ). - In this book, they are called -\series bold -unfunctors -\series default + ***stop using the word +\begin_inset Quotes eld +\end_inset + +unfunctor +\begin_inset Quotes erd +\end_inset + + and instead use +\begin_inset Quotes eld +\end_inset + +GADT +\begin_inset Quotes erd +\end_inset -\begin_inset Index idx + throughout the book. + *** In this book we call a GADT a type constructor having special values + when its type parameter is set to certain specific types, which makes it + impossible to implement +\begin_inset listings +inline true status open \begin_layout Plain Layout -unfunctor + +map \end_layout \end_inset - for brevity. - An unfunctor is a type constructor having special values when its type - parameter is set to certain specific types. - An example of an unfunctor is: +. + An example of a GADT is: \begin_inset listings inline false status open @@ -9405,24 +9178,6 @@ can \end_inset The corresponding Scala code is: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "55col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -9444,20 +9199,6 @@ def map[A, B]: Q[A] => (A => B) => Q[B] = { qa => f => \end_inset - -\begin_inset VSpace -80baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent This \begin_inset listings inline true @@ -11507,21 +11248,6 @@ identity laws!of contrafunctors \end_inset -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -200baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{1.5pc}\xyScaleX{3pc} & H^{B}\ar[rd]\sp(0.6){\ ~\text{cmap}_{H}(g^{:C\rightarrow B})}\\ @@ -11531,23 +11257,6 @@ H^{A}\ar[ru]\sp(0.4){\text{cmap}_{H}(f^{:B\rightarrow A})\ }\ar[rr]\sb(0.5){\tex \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -200baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Since the function argument \begin_inset Formula $f^{:B\rightarrow A}$ \end_inset @@ -12055,24 +11764,6 @@ Ordinarily, applying a function of type \end_inset is an error: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "53col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -12094,23 +11785,6 @@ h(p) // Type error: need type Q but found P. \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent However, the Scala compiler will admit this kind of code when \begin_inset Formula $P$ \end_inset @@ -12783,21 +12457,6 @@ Two \end_inset with no additional code written by the programmer: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -12843,23 +12502,6 @@ res0: Option[Int] = Some(10) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent We may imagine that the compiler automatically uses the subtype conversion function \begin_inset listings @@ -12910,9 +12552,9 @@ f2 \end_inset - is equivalent to an identity function, the subtype conversion does not - change any data and only reassigns some types. - So, the compiler does not need to insert any additional machine code. + is equivalent to an identity function, this conversion does not change + any data and only reassigns some types. + So, the compiler will not need to insert any additional machine code. The subtype conversion does not lead to any decrease in performance. \end_layout @@ -13567,12 +13209,12 @@ noprefix "false" \end_layout \begin_layout Subsection -Solved examples: implementation of functors and contrafunctors +Examples: implementing functors and contrafunctors \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -13627,21 +13269,6 @@ Option[_] \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "66col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -13658,7 +13285,7 @@ def map[A, B](oa: Option[A])(f: A => B): Option[B] = oa match { \begin_layout Plain Layout - case Some(x: Int) => Some(f((x+1).asInstanceOf[A])) + case Some(x: Int) => Some(f((x + 1).asInstanceOf[A])) \end_layout \begin_layout Plain Layout @@ -13673,23 +13300,6 @@ def map[A, B](oa: Option[A])(f: A => B): Option[B] = oa match { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent This code performs a non-standard computation if the type parameter \begin_inset listings inline true @@ -15942,20 +15552,18 @@ map \end_inset as a class method: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "37.9col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - +\family default +\series default +\shape default +\size default +\emph default +\bar default +\strikeout default +\xout default +\uuline default +\uwave default +\noun default +\color inherit \begin_inset listings inline false @@ -15973,23 +15581,6 @@ x.map(f).map(g) == x.map(f andThen g) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent To take advantage of this syntax, we use the \begin_inset Index idx status open @@ -16574,22 +16165,7 @@ fmap \end_inset -This equation is illustrated by the type diagram below. -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - +This equation is illustrated by the type diagram: \begin_inset Preview \begin_layout Standard @@ -16604,18 +16180,6 @@ F^{A,B}\ar[ru]\sp(0.4){\text{fmap}_{F^{\bullet,B}}(f^{:A\rightarrow C})~~~}\ar[r \end_inset -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -220baselineskip% -\end_inset - - \end_layout \end_inset @@ -17852,21 +17416,6 @@ fmap \end_inset is defined by: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "59.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -17888,27 +17437,10 @@ def fmap[A, B](f: A => B): (L[A], M[A]) => (L[B], M[B]) = { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The corresponding code notation is: \begin_inset Formula \[ -\negthickspace\negthickspace f^{\uparrow P}\triangleq l^{:L^{A}}\times m^{:M^{A}}\rightarrow f^{\uparrow L}(l)\times f^{\uparrow M}(m)\quad. +f^{\uparrow P}\triangleq l^{:L^{A}}\times m^{:M^{A}}\rightarrow f^{\uparrow L}(l)\times f^{\uparrow M}(m)\quad. \] \end_inset @@ -21517,7 +21049,7 @@ To verify the composition law: \end_layout \begin_layout Subsection -Solved examples: How to recognize functors and contrafunctors +Examples: Recognizing functors and contrafunctors \begin_inset CommandInset label LatexCommand label name "subsec:Solved-examples:-How-to-recognize-functors" @@ -22076,7 +21608,7 @@ noprefix "false" status collapsed \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset diff --git a/sofp-src/sofp-functors.tex b/sofp-src/sofp-functors.tex index be78b2f91..d7ab5a7c8 100644 --- a/sofp-src/sofp-functors.tex +++ b/sofp-src/sofp-functors.tex @@ -91,12 +91,8 @@ \subsection{Extended example: \texttt{Option} and the identity law\label{subsec: \] This function produces a new \lstinline!Option[B]! value that wraps transformed data. We will now use this example to develop intuition -about manipulating data in a wrapper. - -Two possible implementations of \lstinline!map! fit the type signature: - -\begin{wrapfigure}{l}{0.585\columnwidth}% -\vspace{-0.7\baselineskip} +about manipulating data in a wrapper. Two possible implementations +of \lstinline!map! will fit the type signature: \begin{lstlisting} def mapX[A, B](oa: Option[A])(f: A => B): Option[B] = None @@ -106,10 +102,7 @@ \subsection{Extended example: \texttt{Option} and the identity law\label{subsec: case Some(x) => Some(f(x)) } \end{lstlisting} -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent The code of \lstinline!mapX! loses information\index{information loss} +The code of \lstinline!mapX! loses information\index{information loss} since it always returns \lstinline!None! and ignores all input. The implementation \lstinline!mapY! is more useful since it preserves information. @@ -122,9 +115,6 @@ \subsection{Extended example: \texttt{Option} and the identity law\label{subsec: to a value wrapped in an \lstinline!Option[A]! should not change that value. To verify that, substitute the identity function instead of \lstinline!f! into \lstinline!mapY! and compute: - -\begin{wrapfigure}{l}{0.58\columnwidth}% -\vspace{-0.6\baselineskip} \begin{lstlisting} mapY[A, A](x: Option[A])(identity[A]: A => A): Option[A] == x match { @@ -132,17 +122,12 @@ \subsection{Extended example: \texttt{Option} and the identity law\label{subsec: case Some(x) => Some(x) // No change. } == x \end{lstlisting} -\vspace{0.1\baselineskip} -\end{wrapfigure}% - -\noindent The result is always equal to \lstinline!x!. We can write -that fact as an equation:\vspace{-0.3\baselineskip} +The result is always equal to \lstinline!x!. We can write that fact +as an equation: \[ \forall x^{:\text{Opt}^{A}}.\,\,\text{map}\,(x)(\text{id})=x\quad. \] -\vspace{-0.85\baselineskip} - -\noindent This equation is called the \textbf{identity law}\index{identity laws!of functors} +This equation is called the \textbf{identity law}\index{identity laws!of functors} of \lstinline!map!. The identity law is a formal way of expressing the information-preserving property of the \lstinline!map! function. The implementation \lstinline!mapX! violates the identity law since @@ -150,7 +135,7 @@ \subsection{Extended example: \texttt{Option} and the identity law\label{subsec: and not equal to \lstinline!oa! for arbitrary values of \lstinline!oa!. A data wrapper should not unexpectedly lose information when we manipulate the wrapped data. So, the correct implementation of \lstinline!map! -is \lstinline!mapY!. The code notation for \lstinline!map! is:\vspace{-0.4\baselineskip} +is \lstinline!mapY!. The code notation for \lstinline!map! is: \[ \text{map}^{A,B}\triangleq p^{:\bbnum 1+A}\rightarrow f^{:A\rightarrow B}\rightarrow p\triangleright\,\begin{array}{|c||cc|} & \bbnum 1 & B\\ @@ -169,19 +154,12 @@ \subsection{Extended example: \texttt{Option} and the identity law\label{subsec: \] The Scala implementation and the code notation for \lstinline!fmap! are shorter than those for \lstinline!map!: - -\begin{wrapfigure}{l}{0.55\columnwidth}% -\vspace{-0\baselineskip} \begin{lstlisting} def fmap[A, B](f: A => B): Option[A] => Option[B] = { case None => None case Some(x) => Some(f(x)) } \end{lstlisting} -\vspace{-2\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.7\baselineskip} \begin{equation} \text{fmap}\,(f^{:A\rightarrow B})\triangleq\,\begin{array}{|c||cc|} & \bbnum 1 & B\\ @@ -189,7 +167,6 @@ \subsection{Extended example: \texttt{Option} and the identity law\label{subsec: A & \bbnum 0 & f \end{array}\quad.\label{eq:f-def-opt-fmap-matrix-notation} \end{equation} -\vspace{-0.5\baselineskip} The identity law also looks simpler if expressed in terms of \lstinline!fmap!, namely $\text{fmap}\,(\text{id})=\text{id}$. In writing $\text{fmap}\,(\text{id})=\text{id}$, @@ -237,17 +214,11 @@ \subsection{Motivation for the composition law} res1: Option[Int] = Some(2) \end{lstlisting} This result is the same as when applying a lifted function $x\rightarrow x+2$: - -\begin{wrapfigure}{l}{0.3\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} scala> c.map(x => x + 2) res2: Option[Int] = Some(2) \end{lstlisting} -\vspace{-1.2\baselineskip} -\end{wrapfigure}% - -\noindent It would be confusing and counter-intuitive if \lstinline!c.map(x => x + 2)! +It would be confusing and counter-intuitive if \lstinline!c.map(x => x + 2)! did not give the same result as \lstinline!c.map(incr).map(incr)!. We can formulate this property more generally: liftings should preserve @@ -296,9 +267,6 @@ \subsection{Motivation for the composition law} Now we consider the left-hand side of the law, $\text{fmap}\,(f)\bef\text{fmap}\,(g)$, and write the Scala expressions: - -\begin{wrapfigure}{l}{0.33\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} oa.map(f).map(g) == (oa match { case None => None @@ -314,13 +282,10 @@ \subsection{Motivation for the composition law} case Some(x) => g(f(x)) } \end{lstlisting} -\vspace{-5\baselineskip} -\end{wrapfigure}% - -\noindent We find that the two sides of the law have identical code. +We find that the two sides of the law have identical code. The derivation is shorter in the matrix notation. We use Eq.~(\ref{eq:f-def-opt-fmap-matrix-notation}) -as the definition of \lstinline!fmap! and omit the types:\vspace{-0.45\baselineskip} +as the definition of \lstinline!fmap! and omit the types: \begin{align*} & \text{fmap}\,(f)\bef\text{fmap}\,(g)=\,\begin{array}{||cc|} \text{id} & \bbnum 0\\ @@ -338,15 +303,14 @@ \subsection{Motivation for the composition law} \end{array}\\ {\color{greenunder}\text{definition of fmap}:}\quad & =\text{fmap}\,(f\bef g)\quad. \end{align*} -\vspace{-1.4\baselineskip} - -\noindent These calculations prove that the \lstinline!map! method -of the \lstinline!Option! type satisfies the composition law. If -the composition law did not hold, we would incorrectly imagine the -way \lstinline!map! manipulates data within the \lstinline!Option! -wrapper. Looking at the Scala code example above, we expect \lstinline!c.map(incr).map(incr)! -to increment the data wrapped by \lstinline!c! two times. If the -result of \lstinline!c.map(incr).map(incr)! were not \lstinline!Some(2)! + +These calculations prove that the \lstinline!map! method of the \lstinline!Option! +type satisfies the composition law. If the composition law did not +hold, we would incorrectly imagine the way \lstinline!map! manipulates +data within the \lstinline!Option! wrapper. Looking at the Scala +code example above, we expect \lstinline!c.map(incr).map(incr)! to +increment the data wrapped by \lstinline!c! two times. If the result +of \lstinline!c.map(incr).map(incr)! were not \lstinline!Some(2)! but, say, \lstinline!Some(1)! or \lstinline!None!, our ordinary intuitions about data transformations would become incorrect. In other words, violations of the composition law prevent us from understanding @@ -393,8 +357,8 @@ \subsection{Functors: definition and examples\label{subsec:Functors:-definition- \] \item Two laws that the function \lstinline!fmap! must satisfy: \begin{align} -{\color{greenunder}\text{identity law of }L:}\quad & \text{fmap}_{L}(\text{id}^{:A\rightarrow A})=\text{id}^{:L^{A}\rightarrow L^{A}}\quad,\label{eq:f-identity-law-functor-fmap}\\ -{\color{greenunder}\text{composition law of }L:}\quad & \text{fmap}_{L}(f^{:A\rightarrow B}\bef g^{:B\rightarrow C})=\text{fmap}_{L}(f^{:A\rightarrow B})\bef\text{fmap}_{L}(g^{:B\rightarrow C})\quad.\label{eq:f-composition-law-functor-fmap} +{\color{greenunder}\text{identity law}:}\quad & \text{fmap}_{L}(\text{id}^{:A\rightarrow A})=\text{id}^{:L^{A}\rightarrow L^{A}}\quad,\label{eq:f-identity-law-functor-fmap}\\ +{\color{greenunder}\text{composition law}:}\quad & \text{fmap}_{L}(f^{:A\rightarrow B}\bef g^{:B\rightarrow C})=\text{fmap}_{L}(f^{:A\rightarrow B})\bef\text{fmap}_{L}(g^{:B\rightarrow C})\quad.\label{eq:f-composition-law-functor-fmap} \end{align} \end{itemize} A type constructor $L^{\bullet}$ with these properties is called @@ -407,17 +371,13 @@ \subsection{Functors: definition and examples\label{subsec:Functors:-definition- is a directed graph whose vertices are types and edges are functions mapping one type to another. Function composition corresponds to following a path in the diagram. A type diagram for the composition law~(\ref{eq:f-composition-law-functor-fmap}) -is shown\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-1.9\baselineskip} +is: \[ \xymatrix{\xyScaleY{1.5pc}\xyScaleX{3pc} & L^{B}\ar[rd]\sp(0.6){~~\text{fmap}_{L}(g^{:B\rightarrow C})}\\ L^{A}\ar[ru]\sp(0.4){\text{fmap}_{L}(f^{:A\rightarrow B})\ ~}\ar[rr]\sb(0.5){\text{fmap}_{L}(f^{:A\rightarrow B}\bef g^{:B\rightarrow C})\ } & & L^{C} } \] - -\vspace{-2\baselineskip} -\end{wrapfigure}% -at left. There are two paths from $L^{A}$ to $L^{C}$; by Eq.~(\ref{eq:f-composition-law-functor-fmap}), +There are two paths from $L^{A}$ to $L^{C}$; by Eq.~(\ref{eq:f-composition-law-functor-fmap}), both paths must give the same result. Mathematicians call such diagrams \textbf{commutative}\index{commutative diagram}. @@ -509,7 +469,7 @@ \subsection{Functors: definition and examples\label{subsec:Functors:-definition- is a functor. We still need to check that the functor laws hold for it. -\subsubsection{Example \label{subsec:f-Example-Int-x-A}\ref{subsec:f-Example-Int-x-A}\index{solved examples}} +\subsubsection{Example \label{subsec:f-Example-Int-x-A}\ref{subsec:f-Example-Int-x-A}\index{examples (with code)}} Verify that the above implementation of \lstinline!map! for \lstinline!Counted! satisfies the functor laws. @@ -758,8 +718,8 @@ \subsubsection{Example \label{subsec:Example-rec-poly-functor-List}\ref{subsec:E /* expand (f andThen g)(head): */ == g(f(head)) :: fmap(f andThen g)(tail) \end{lstlisting} -Turning now to the code notation, we begin by writing the definition -of the type constructor $\text{List}^{A}$: +We will now write the same proof in the code notation. Begin with +the definition of the type constructor $\text{List}^{A}$: \[ \text{List}^{A}\triangleq\bbnum 1+A\times\text{List}^{A}\quad. \] @@ -966,33 +926,26 @@ \subsection{Functor block expressions\index{functor block}} \end{lstlisting} A confusing feature of the \lstinline!for! / \lstinline!yield! syntax -is that, at first sight, functor blocks (such as the code\begin{wrapfigure}{l}{0.36\columnwidth}% -\vspace{-0.8\baselineskip} +is that, at first sight, functor blocks (such as this code: \begin{lstlisting} for { x <- p; ... } yield expr(x) \end{lstlisting} - -\vspace{-0.8\baselineskip} -\end{wrapfigure}% - shown at left) appear to compute, or to \textsf{``}yield\textsf{''}, the value \lstinline!expr(x)!. -However, this is not so. As the above examples show, if \lstinline!p! -is a sequence then the functor block also computes a \emph{sequence}. -In general, the result of a functor block is a \textsf{``}wrapped\textsf{''} value, -where the type of the \textsf{``}wrapper\textsf{''} is determined by the first line -of the functor block. The first line must have a left arrow followed -by a \textsf{``}source\index{functor block!source}\textsf{''}, which must be an expression -of a functor type, i.e., of type \lstinline!L[A]! for some functor -\lstinline!L[_]!. The result\textsf{'}s type will be \lstinline!L[B]! where -\lstinline!B! is the type of the expression after the \lstinline!yield! +appear to compute the value \lstinline!expr(x)! because the code +says \lstinline!yield expr(x)!. However, this is not so. As the above +examples show, if \lstinline!p! is a sequence then the functor block +also computes a \emph{sequence}. In general, the result of a functor +block is a \textsf{``}wrapped\textsf{''} value, where the type of the \textsf{``}wrapper\textsf{''} +is determined by the first line of the functor block. The first line +must have a left arrow followed by a \textsf{``}source\index{functor block!source}\textsf{''}, +which must be an expression of a functor type, i.e., of type \lstinline!L[A]! +for some functor \lstinline!L[_]!. The result\textsf{'}s type will be \lstinline!L[B]! +where \lstinline!B! is the type of the expression after the \lstinline!yield! keyword. For instance, the first line of the following functor block contains an \lstinline!Option! value, \lstinline!Some(123)!, as the \textsf{``}source\textsf{''}. Because of that, the value of the entire functor block expression will also be of type \lstinline!Option!: - -\begin{wrapfigure}{l}{0.47\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} scala> for { x <- Some(123) // "Source" is Option[Int]. @@ -1000,17 +953,12 @@ \subsection{Functor block expressions\index{functor block}} } yield { if (y > 0) s"Have $y" else "Error" } res1: Option[String] = Some(Have 12) \end{lstlisting} - -\vspace{-1.2\baselineskip} -\end{wrapfigure}% - -\noindent In this code, the \lstinline!yield! keyword is followed -by an expression of type \lstinline!String!. So, the result of the -entire functor block is of type \lstinline!Option[String]!. Note -that the expression after the \textsf{``}\lstinline!yield!\textsf{''} can be a block -of arbitrary code containing new \lstinline!val!s, new \lstinline!def!s, -and/or other \lstinline!for!/\lstinline!yield! functor blocks if -needed. +In this code, the \lstinline!yield! keyword is followed by an expression +of type \lstinline!String!. So, the result of the entire functor +block is of type \lstinline!Option[String]!. Note that the expression +after the \textsf{``}\lstinline!yield!\textsf{''} can be a block of arbitrary code +containing new \lstinline!val!s, new \lstinline!def!s, and/or other +\lstinline!for!/\lstinline!yield! functor blocks if needed. Functor blocks can be used with any functor that has a \lstinline!map! method, not only with library-defined type constructors such as \lstinline!Seq! @@ -1291,7 +1239,7 @@ \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} ~\vspace{-1.08\baselineskip} \[ -\quad\quad\quad\quad\quad\quad\quad\quad H^{A}\triangleq A\rightarrow\text{Int}\quad. +\quad\quad\quad H^{A}\triangleq A\rightarrow\text{Int}\quad. \] The data type \lstinline!H[A]! does not wrap data of type $A$; instead, it is a function that \emph{consumes} data of type $A$. One cannot @@ -1320,10 +1268,12 @@ \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} Another important example where the \lstinline!map!\textsf{'}s type signature cannot be implemented are certain kinds of type constructors called \index{generalized algebraic data types}\textbf{generalized algebraic -data types} (GADTs\index{GADT}). In this book, they are called \textbf{unfunctors}\index{unfunctor} -for brevity. An unfunctor is a type constructor having special values -when its type parameter is set to certain specific types. An example -of an unfunctor is: +data types} (\textbf{GADT}s\index{GADT}). {*}{*}{*}stop using the +word \textsf{``}unfunctor\textsf{''} and instead use \textsf{``}GADT\textsf{''} throughout the book. +{*}{*}{*} In this book we call a GADT a type constructor having special +values when its type parameter is set to certain specific types, which +makes it impossible to implement \lstinline!map!. An example of a +GADT is: \begin{lstlisting} sealed trait ServerAction[R] final case class StoreId(x: Long, y: String) extends ServerAction[Boolean] @@ -1377,22 +1327,15 @@ \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} \text{map}^{A,B}\triangleq q^{:A\rightarrow\text{Int}}\times a^{:A}\rightarrow f^{:A\rightarrow B}\rightarrow(\_\rightarrow q(a))^{:B\rightarrow\text{Int}}\times f(a)\quad. \] The corresponding Scala code is: - -\begin{wrapfigure}{l}{0.55\columnwidth}% -\vspace{-0.6\baselineskip} - \begin{lstlisting} def map[A, B]: Q[A] => (A => B) => Q[B] = { qa => f => Q[B](_ => qa.q(qa.a), f(qa.a)) } \end{lstlisting} -\vspace{-0.8\baselineskip} -\end{wrapfigure}% - -\noindent This \lstinline!map! function is fully parametric (since -it treats the type \lstinline!Int! as a type parameter) and has the -right type signature, but the functor laws do not hold. To show that -the identity law fails, we consider an arbitrary value $q^{:A\rightarrow\text{Int}}\times a^{:A}$ +This \lstinline!map! function is fully parametric (since it treats +the type \lstinline!Int! as a type parameter) and has the right type +signature, but the functor laws do not hold. To show that the identity +law fails, we consider an arbitrary value $q^{:A\rightarrow\text{Int}}\times a^{:A}$ and compute: \begin{align*} {\color{greenunder}\text{expect to equal }q\times a:}\quad & \text{map}\,(q\times a)(\text{id})\\ @@ -1683,22 +1626,15 @@ \subsection{Contrafunctors\label{subsec:Contrafunctors}} {\color{greenunder}\text{identity law}:}\quad & \text{cmap}^{A,A}(\text{id}^{:A\rightarrow A})=\text{id}^{:H^{A}\rightarrow H^{A}}\quad,\\ {\color{greenunder}\text{composition law}:}\quad & \text{cmap}^{A,B}(f^{:B\rightarrow A})\bef\text{cmap}^{B,C}(g^{:C\rightarrow B})=\text{cmap}^{A,C}(g\bef f)\quad. \end{align*} - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-2\baselineskip} \[ \xymatrix{\xyScaleY{1.5pc}\xyScaleX{3pc} & H^{B}\ar[rd]\sp(0.6){\ ~\text{cmap}_{H}(g^{:C\rightarrow B})}\\ H^{A}\ar[ru]\sp(0.4){\text{cmap}_{H}(f^{:B\rightarrow A})\ }\ar[rr]\sb(0.5){\text{cmap}_{H}(g^{:C\rightarrow B}\bef f^{:B\rightarrow A})\ ~} & & H^{C} } \] - -\vspace{-2\baselineskip} -\end{wrapfigure}% - -\noindent Since the function argument $f^{:B\rightarrow A}$ has the -reverse order of types, the composition law needs the reverse order -of composition $\left(g\bef f\right)$ on one side; in this way, all -types match. To verify the identity law: +Since the function argument $f^{:B\rightarrow A}$ has the reverse +order of types, the composition law needs the reverse order of composition +$\left(g\bef f\right)$ on one side; in this way, all types match. +To verify the identity law: \begin{align*} {\color{greenunder}\text{expect to equal }\text{id}:}\quad & \text{cmap}\left(\text{id}\right)\\ {\color{greenunder}\text{use Eq.~(\ref{eq:f-example-1-contrafmap})}:}\quad & =h\rightarrow\gunderline{(\text{id}\bef h)}\\ @@ -1805,21 +1741,13 @@ \subsection{Subtyping, covariance, and contravariance\label{subsec:Covariance,-c Ordinarily, applying a function of type $Q\rightarrow R$ to a value of type $P$ is an error: - -\begin{wrapfigure}{l}{0.53\columnwidth}% -\vspace{-0.8\baselineskip} - \begin{lstlisting} def h(q: Q): R = ??? val p: P = ??? h(p) // Type error: need type Q but found P. \end{lstlisting} - -\vspace{-0.8\baselineskip} -\end{wrapfigure}% - -\noindent However, the Scala compiler will admit this kind of code -when $P$ is a \textsf{``}subtype\textsf{''} of $Q$. +However, the Scala compiler will admit this kind of code when $P$ +is a \textsf{``}subtype\textsf{''} of $Q$. Programming languages that support subtyping allow us to use a value of type $P$ in any expression instead of a value of type $Q$, when @@ -1943,9 +1871,6 @@ \subsubsection{Definition \label{subsec:Definition-subtyping}\ref{subsec:Definit function with argument of type \lstinline!AtMostTwo! can be applied to a value of type \lstinline!Two! with no additional code written by the programmer: - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.5\baselineskip} \begin{lstlisting} def head: AtMostTwo => Option[Int] = { case Zero() => None @@ -1956,18 +1881,13 @@ \subsubsection{Definition \label{subsec:Definition-subtyping}\ref{subsec:Definit scala> head(Two(10, 20)) res0: Option[Int] = Some(10) \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent We may imagine that the compiler automatically uses the -subtype conversion function \lstinline!f2! shown above to convert -a value of the type \lstinline!Two! into a value of the type \lstinline!AtMostTwo!. +We may imagine that the compiler automatically uses the subtype conversion +function \lstinline!f2! shown above to convert a value of the type +\lstinline!Two! into a value of the type \lstinline!AtMostTwo!. Since the code of \lstinline!f2! is equivalent to an identity function, -the subtype conversion does not change any data and only reassigns -some types. So, the compiler does not need to insert any additional -machine code. The subtype conversion does not lead to any decrease -in performance. +this conversion does not change any data and only reassigns some types. +So, the compiler will not need to insert any additional machine code. +The subtype conversion does not lead to any decrease in performance. The void type (\lstinline!Nothing!, denoted by $\bbnum 0$ in the type notation) is special:\index{void type} it is a subtype of \emph{every} @@ -2054,29 +1974,22 @@ \subsubsection{Definition \label{subsec:Definition-subtyping}\ref{subsec:Definit The next examples confirm this intuition, which will be made rigorous in Section~\ref{subsec:Solved-examples:-How-to-recognize-functors}. -\subsection{Solved examples: implementation of functors and contrafunctors\index{solved examples}} +\subsection{Examples: implementing functors and contrafunctors\index{examples (with code)}} \subsubsection{Example \label{subsec:f-Example-functors}\ref{subsec:f-Example-functors}} Consider this implementation of \lstinline!map! for the type constructor \lstinline!Option[_]!: - -\begin{wrapfigure}{l}{0.66\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} def map[A, B](oa: Option[A])(f: A => B): Option[B] = oa match { case None => None - case Some(x: Int) => Some(f((x+1).asInstanceOf[A])) + case Some(x: Int) => Some(f((x + 1).asInstanceOf[A])) case Some(x) => Some(f(x)) } \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent This code performs a non-standard computation if the type -parameter \lstinline!A! is set to \lstinline!Int!. Show that this -implementation of \lstinline!map! violates the functor laws. +This code performs a non-standard computation if the type parameter +\lstinline!A! is set to \lstinline!Int!. Show that this implementation +of \lstinline!map! violates the functor laws. \subparagraph{Solution} @@ -2490,18 +2403,11 @@ \subsection{Functor laws in the pipe notation} \end{align*} The laws are easier to read when using \lstinline!map! as a class method: - -\begin{wrapfigure}{l}{0.379\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} x.map(identity) == x x.map(f).map(g) == x.map(f andThen g) \end{lstlisting} - -\vspace{-0.5\baselineskip} -\end{wrapfigure}% - -\noindent To take advantage of this syntax, we use the \index{pipe notation}\textbf{pipe +To take advantage of this syntax, we use the \index{pipe notation}\textbf{pipe notation} with the symbol $\triangleright$ (\textsf{``}pipe\textsf{''}). Then $x\triangleright\text{fmap}(f)$ is the same as \lstinline!fmap(f)(x)! and \lstinline!x.map(f)!. Then the functor laws become: @@ -2623,19 +2529,13 @@ \subsection{Bifunctors\label{subsec:Bifunctors}} \begin{equation} \text{fmap}_{F^{\bullet,B}}(f^{:A\rightarrow C})\bef\text{fmap}_{F^{C,\bullet}}(g^{:B\rightarrow D})=\text{fmap}_{F^{A,\bullet}}(g^{:B\rightarrow D})\bef\text{fmap}_{F^{\bullet,D}}(f^{:A\rightarrow C})\quad.\label{eq:f-fmap-fmap-bifunctor-commutativity} \end{equation} -This equation is illustrated by the type diagram below. - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-1.5\baselineskip} +This equation is illustrated by the type diagram: \[ \xymatrix{\xyScaleY{2.0pc}\xyScaleX{5.0pc} & F^{C,B}\ar[rd]\sp(0.6){\ ~~\text{fmap}_{F^{C,\bullet}}(g^{:B\rightarrow D})}\\ F^{A,B}\ar[ru]\sp(0.4){\text{fmap}_{F^{\bullet,B}}(f^{:A\rightarrow C})~~~}\ar[rd]\sb(0.5){\text{fmap}_{F^{A,\bullet}}(g^{:B\rightarrow D})~~\ }\ar[rr]\sp(0.5){\text{bimap}_{F}(f^{:A\rightarrow C})(g^{:B\rightarrow D})\ } & & F^{C,D}\\ & F^{A,D}\ar[ru]\sb(0.6){~~~~\text{fmap}_{F^{\bullet,D}}(f^{:A\rightarrow C})} } \] - -\vspace{-2.2\baselineskip} -\end{wrapfigure}% Different paths in this diagram give the same results if they arrive at the same vertex (as mathematicians say, \textsf{``}the diagram is \index{commutative diagram}commutative\textsf{''}). In this way, the diagram illustrates at once the commutativity law~(\ref{eq:f-fmap-fmap-bifunctor-commutativity}) @@ -2825,21 +2725,14 @@ \subsubsection{Statement \label{subsec:functor-Statement-functor-product}\ref{su \subparagraph{Proof} The \lstinline!fmap! function for $P$ is defined by: - -\begin{wrapfigure}{l}{0.595\columnwidth}% -\vspace{-0.9\baselineskip} \begin{lstlisting} def fmap[A, B](f: A => B): (L[A], M[A]) => (L[B], M[B]) = { case (la, ma) => (la.map(f), ma.map(f)) } \end{lstlisting} - -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent The corresponding code notation is: +The corresponding code notation is: \[ -\negthickspace\negthickspace f^{\uparrow P}\triangleq l^{:L^{A}}\times m^{:M^{A}}\rightarrow f^{\uparrow L}(l)\times f^{\uparrow M}(m)\quad. +f^{\uparrow P}\triangleq l^{:L^{A}}\times m^{:M^{A}}\rightarrow f^{\uparrow L}(l)\times f^{\uparrow M}(m)\quad. \] Writing this code using the pipe ($\triangleright$) operation makes it somewhat closer to the Scala syntax: @@ -3495,7 +3388,7 @@ \subsubsection{Statement \label{subsec:functor-Statement-contrafunctor-recursive \end{align*} -\subsection{Solved examples: How to recognize functors and contrafunctors\label{subsec:Solved-examples:-How-to-recognize-functors}} +\subsection{Examples: Recognizing functors and contrafunctors\label{subsec:Solved-examples:-How-to-recognize-functors}} Sections~\ref{subsec:f-Functor-constructions} and~\ref{subsec:f-Contrafunctor-constructions} describe how functors and contrafunctors are built from other type @@ -3586,7 +3479,7 @@ \subsection{Solved examples: How to recognize functors and contrafunctors\label{ or prove the laws for each new functor and contrafunctor. The next examples illustrate this procedure on a simpler type constructor. -\subsubsection{Example \label{subsec:f-Example-recognize-type-variance-1}\ref{subsec:f-Example-recognize-type-variance-1}\index{solved examples}} +\subsubsection{Example \label{subsec:f-Example-recognize-type-variance-1}\ref{subsec:f-Example-recognize-type-variance-1}\index{examples (with code)}} Rewrite these Scala definitions in the type notation and decide whether $F$ and $G$ are covariant or contravariant with respect to each diff --git a/sofp-src/sofp-higher-order-functions.lyx b/sofp-src/sofp-higher-order-functions.lyx index 73ee532d0..af92d675a 100644 --- a/sofp-src/sofp-higher-order-functions.lyx +++ b/sofp-src/sofp-higher-order-functions.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -946,7 +961,7 @@ status open \begin_layout Plain Layout -val f: (Int => Int) = { +val f: (Int => Int) = { // Parentheses around (Int => Int) are optional. \end_layout \begin_layout Plain Layout @@ -1020,22 +1035,6 @@ q = 20 \end_inset are captured. -\end_layout - -\begin_layout Standard -The parentheses around a function's type, such as -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -(Int => Int) -\end_layout - -\end_inset - -, are optional in Scala. \end_layout @@ -1680,7 +1679,7 @@ def foldLeft[A, R](xs: Seq[A])(init: R)(update: (R, A) => R): R \end_inset The type signatures of these functions can be also written equivalently - as: + without argument names, although this is less convenient in practical coding: \begin_inset listings inline false status open @@ -2246,11 +2245,7 @@ val f1: Int => Int => Int = { x => y => x - y } \end_inset - -\end_layout - -\begin_layout Standard -Let us compare the function +Let us rewrite \begin_inset listings inline true status open @@ -2262,7 +2257,7 @@ f1 \end_inset - with a function that takes its two arguments at once, e.g.: + as a function that takes its two arguments at once: \begin_inset listings inline false status open @@ -2299,7 +2294,7 @@ status open \end_inset . - The syntax for using + Calling \begin_inset listings inline true status open @@ -2323,7 +2318,7 @@ f2 \end_inset - is different: + requires different syntax: \begin_inset listings inline false status open @@ -2474,7 +2469,7 @@ full \end_layout \begin_layout Standard -If we need to partially apply an +To partially apply an \emph on uncurried \emph default @@ -2483,21 +2478,6 @@ uncurried \end_inset ) symbol: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - \begin_inset listings lstparams "numbers=left" inline false @@ -2529,20 +2509,6 @@ res3: Int = 16 \end_inset - -\begin_inset VSpace -95baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent (The type annotation \begin_inset listings inline true @@ -2636,7 +2602,7 @@ status open \begin_layout Plain Layout -scala> val r3: Int => Int = { x => f2(20, x) } // Same as r2 above. +scala> val r3: Int => Int = { x => f2(20, x) } // Same as r2 above. \end_layout \begin_layout Plain Layout @@ -3000,21 +2966,6 @@ f1 \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "42col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -3045,20 +2996,6 @@ f2u: (Int, Int) => Int = \end_inset - -\begin_inset VSpace -95baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The syntax \begin_inset listings inline true @@ -3145,7 +3082,7 @@ uncurried \end_inset - are quick to implement, as we will show in Section + are quick to implement (see Section \begin_inset space ~ \end_inset @@ -3159,7 +3096,7 @@ noprefix "false" \end_inset -. + below). These functions are called the \series bold currying @@ -3667,21 +3604,6 @@ swap \end_inset to make it fully parametric: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -55baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -3703,20 +3625,6 @@ def swap[A, B](p: (A, B)): (B, A) = p match { \end_inset - -\begin_inset VSpace -95baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Converting \begin_inset listings inline true @@ -4145,8 +4053,8 @@ status open \begin_layout Plain Layout -def swapAC[A,B,C]: ((A, B, C)) => (C, B, A) = { case (x, y, z) => (z, y, - x) } +def swapAC[A, B, C]: ((A, B, C)) => (C, B, A) = { case (x, y, z) => (z, + y, x) } \end_layout \end_inset @@ -4707,7 +4615,7 @@ This book denotes the forward composition by the symbol \begin_inset Formula ${\displaystyle \bef}$ \end_inset - (pronounced + (which can be read as \begin_inset Quotes eld \end_inset @@ -4716,7 +4624,27 @@ before \end_inset ). - We write: + We define +\begin_inset Formula $f\bef g$ +\end_inset + + (reads +\begin_inset Quotes eld +\end_inset + + +\begin_inset Formula $f$ +\end_inset + + before +\begin_inset Formula $g$ +\end_inset + + +\begin_inset Quotes erd +\end_inset + +) as: \begin_inset Formula \begin{equation} f\bef g\triangleq x\rightarrow g(f(x))\quad.\label{eq:def-of-forward-composition} @@ -5416,15 +5344,21 @@ The last step works since \begin_inset Formula $x\rightarrow f(x)$ \end_inset - is a function taking an argument + is a function that takes an argument \begin_inset Formula $x$ \end_inset - and applying + and applies \begin_inset Formula $f$ \end_inset - to that argument; i.e., + to that argument. + This is the same function as +\begin_inset Formula $f$ +\end_inset + +. + We say that \begin_inset Formula $x\rightarrow f(x)$ \end_inset @@ -5964,7 +5898,15 @@ Program design by calculation \begin_inset Quotes erd \end_inset -, see + by J. +\begin_inset space ~ +\end_inset + +N. +\begin_inset space ~ +\end_inset + +Oliveira where the backward composition is used exclusively, see \family typewriter \begin_inset CommandInset href @@ -6349,21 +6291,6 @@ f_2 \end_inset are not equal: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "17col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -6395,22 +6322,9 @@ res4: Int = 0 \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\noindent It is important that we are able to detect that \begin_inset listings inline true @@ -6565,21 +6479,6 @@ To run this computation in Scala, we need to add a type annotation to the . The code is: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "31.1col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -6597,22 +6496,9 @@ res0: Int = 12 \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -130baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\noindent Curried function calls such as \begin_inset Formula $f(x)(y)$ \end_inset @@ -7103,7 +6989,7 @@ values \begin_inset Formula $f\rightarrow f(9)$ \end_inset - to a nameless function + to the nameless function \begin_inset Formula $x\rightarrow x\%\,4$ \end_inset @@ -7170,21 +7056,6 @@ To verify this result in Scala, we need to specify a type annotation for \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -7201,25 +7072,11 @@ res2: Int = 1 \end_inset - -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent No type annotation is needed for \begin_inset Formula $x\rightarrow x\%\,4$ \end_inset - since the Scala compiler already knows the type of + because the Scala compiler already knows the type of \begin_inset Formula $f$ \end_inset @@ -7263,8 +7120,7 @@ Function expressions group everything to the right: \end_layout \begin_layout Itemize -Function calls group everything to the left. - So, +Function calls group everything to the left: \begin_inset Formula $f(x)(y)(z)$ \end_inset @@ -7272,11 +7128,12 @@ Function calls group everything to the left. \begin_inset Formula $\big((f(x))(y)\big)(z)$ \end_inset -, i.e., +. + The expression \begin_inset Formula $f(x)$ \end_inset - gives a new function that is applied to + is a new function that is applied to \begin_inset Formula $y$ \end_inset @@ -7619,21 +7476,6 @@ Finally, we need to make sure that the types match in the function . We know enough to write the Scala code now: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "68.85col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -7650,20 +7492,6 @@ res6: Int = 6 \end_inset - -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Type annotations for \begin_inset Formula $p$ \end_inset @@ -7676,7 +7504,7 @@ Type annotations for \begin_inset Formula $x$ \end_inset - may be omitted: Scala's compiler will figure out the missing types from + may be omitted: Scala's compiler can figure out the missing types from the given type of \begin_inset Formula $f$ \end_inset @@ -7686,12 +7514,12 @@ Type annotations for \end_layout \begin_layout Subsection -Solved examples: Deriving a function's type from its code +Examples: Deriving a function's type from its code \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -8540,9 +8368,6 @@ twice into a fully parametric function means replacing its type signature by a fully parameterized type signature while keeping the function body unchanged: -\end_layout - -\begin_layout Standard \begin_inset listings inline false status open @@ -8556,52 +8381,6 @@ def twice[A, B, ...](f: ...): ... \end_inset -\end_layout - -\begin_layout Standard -\begin_inset Note Note -status open - -\begin_layout Plain Layout -\begin_inset Wrap figure -lines 0 -placement i -overhang 0in -width "52.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -def twice[A, B, ...](f: ...): ... - = { x => f(f(x)) } -\end_layout - -\end_inset - - -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard @@ -8706,21 +8485,6 @@ twice \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "51col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -8732,20 +8496,6 @@ def twice[A](f: A => A): A => A = { x => f(f(x)) } \end_inset - -\begin_inset VSpace -95baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent This fully parametric function can be written in the code notation as: \begin_inset Formula \begin{equation} @@ -10468,7 +10218,7 @@ A => B => C \begin_layout Plain Layout \size small -type of a curried function +the type of a curried function \end_layout \end_inset @@ -10623,16 +10373,15 @@ What can we do using this chapter's techniques? \end_layout \begin_layout Itemize -Implement functions that return new functions and/or take functions as arguments. +Make functions that return new functions and/or take functions as arguments. \end_layout \begin_layout Itemize -Simplify function expressions symbolically when functions are applied to - arguments. +Simplify expressions symbolically when functions are applied to arguments. \end_layout \begin_layout Itemize -Infer the most general type for a given code expression (perform type inference). +Derive a general type for a given code expression (perform type inference). \end_layout \begin_layout Itemize @@ -10644,12 +10393,12 @@ The following examples and exercises illustrate these techniques. \end_layout \begin_layout Subsection -Solved examples +Examples \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -11629,7 +11378,7 @@ Int => Int \end_inset - are mandatory since + are mandatory as \begin_inset listings inline true status open @@ -12170,7 +11919,7 @@ Solution \end_layout \begin_layout Standard -Begin by assuming +To begin, assume \begin_inset Formula $f^{:A}$ \end_inset @@ -12332,7 +12081,7 @@ q[C, D] \end_inset . - So, we must set the type parameter + So, we must choose \begin_inset Formula $A$ \end_inset @@ -12358,9 +12107,10 @@ q(q) becomes: \begin_inset Formula -\[ -q^{A,B}(q^{C,D}):\left(\left(C\rightarrow\left(C\rightarrow D\right)\rightarrow D\right)\rightarrow B\right)\rightarrow B\quad,\quad\text{where}\quad A=C\rightarrow\left(C\rightarrow D\right)\rightarrow D\quad. -\] +\begin{align*} +q^{A,B}(q^{C,D}) & :\left(\left(C\rightarrow\left(C\rightarrow D\right)\rightarrow D\right)\rightarrow B\right)\rightarrow B\quad,\\ + & \quad\text{where}\quad A=C\rightarrow\left(C\rightarrow D\right)\rightarrow D\quad. +\end{align*} \end_inset @@ -13026,10 +12776,11 @@ Having established that types match, we can now omit the type annotations and rewrite the code: \begin_inset Formula \begin{align*} - & (f\rightarrow g\rightarrow\gunderline g(\gunderline f))\left(x\rightarrow y\rightarrow y(x)\right)\left(h\rightarrow h(10)\right)\\ -\text{substitute }f=x\rightarrow y\rightarrow y(x)\text{ and }g=h\rightarrow h(10):\quad & =(h\rightarrow\gunderline h(10))\left(x\rightarrow y\rightarrow y(x)\right)\\ -\text{substitute }h:\quad & =(x\rightarrow y\rightarrow y(\gunderline x))(10)\\ -\text{substitute }x:\quad & =y\rightarrow y(10)\quad. + & (\gunderline f\rightarrow g\rightarrow g(\gunderline f))\gunderline{\left(x\rightarrow y\rightarrow y(x)\right)}\left(h\rightarrow h(10)\right)\\ +\text{substitute }f=x\rightarrow y\rightarrow y(x):\quad & =\big(\gunderline g\rightarrow\gunderline g(x\rightarrow y\rightarrow y(x))\big)\gunderline{\left(h\rightarrow h(10)\right)}\\ +\text{substitute }g=h\rightarrow h(10):\quad & =(\gunderline h\rightarrow\gunderline h(10))\gunderline{\left(x\rightarrow y\rightarrow y(x)\right)}\\ +\text{substitute }h=x\rightarrow y\rightarrow y(x):\quad & =(\gunderline x\rightarrow y\rightarrow y(\gunderline x))\gunderline{(10)}\\ +\text{substitute }x=10:\quad & =y\rightarrow y(10)\quad. \end{align*} \end_inset @@ -13047,7 +12798,7 @@ The type of this expression is \begin_inset Formula $y$ \end_inset - is an arbitrary function, we cannot simplify + is an arbitrary function, we cannot simplify either \begin_inset Formula $y(10)$ \end_inset @@ -13231,7 +12982,7 @@ To verify that the function \begin_inset Formula $\text{Int}\rightarrow D$ \end_inset -, say, for +, say, with \begin_inset Formula $D=\text{Boolean}$ \end_inset @@ -13340,20 +13091,25 @@ This allows us to compute the forward compositions left to right: \end_inset -Computing the pairwise combinations in another order, we get: +Computing the pairwise combinations in another order, we get the same result: \begin_inset Formula \begin{align*} & \left(p\rightarrow p(2)\right)\bef\left(z\rightarrow z+3\right)=p\rightarrow p(2)+3\quad.\\ - & \left(x\rightarrow y\rightarrow x(x(y))\right)\bef\left(p\rightarrow p(2)+3\right)=x\rightarrow\left(y\rightarrow x(x(y))\right)(2)+3=x\rightarrow x(x(2))+3\quad. + & \left(x\rightarrow y\rightarrow x(x(y))\right)\bef\left(p\rightarrow p(2)+3\right)=x\rightarrow\left(y\rightarrow x(x(y))\right)(2)+3\\ + & \quad=x\rightarrow x(x(2))+3\quad. \end{align*} \end_inset Types are inferred as: -\begin_inset Formula $\left(x\rightarrow y\rightarrow x(x(y))\right)^{:(\text{Int}\rightarrow\text{Int})\rightarrow(\text{Int}\rightarrow\text{Int})}\bef\left(p\rightarrow p(2)\right)^{:(\text{Int}\rightarrow\text{Int})\rightarrow\text{Int}}\bef\left(z\rightarrow z+3\right)^{:\text{Int}\rightarrow\text{Int}}$ +\begin_inset Formula +\[ +\left(x\rightarrow y\rightarrow x(x(y))\right)^{:(\text{Int}\rightarrow\text{Int})\rightarrow(\text{Int}\rightarrow\text{Int})}\bef\left(p\rightarrow p(2)\right)^{:(\text{Int}\rightarrow\text{Int})\rightarrow\text{Int}}\bef\left(z\rightarrow z+3\right)^{:\text{Int}\rightarrow\text{Int}}\quad. +\] + \end_inset -. + \end_layout \begin_layout Subsubsection @@ -13515,8 +13271,7 @@ noprefix "false" \end_inset -, implementing it as a curried function and replacing the hard-coded number - +, making it a curried function and replacing the hard-coded number \begin_inset Formula $100$ \end_inset @@ -14712,7 +14467,15 @@ and \end_layout \begin_layout Standard -The following examples illustrate the concept of a function's order. +The following examples illustrate the concept of a function's +\begin_inset Quotes eld +\end_inset + +order +\begin_inset Quotes erd +\end_inset + +. Consider the code: \begin_inset listings inline false @@ -14896,7 +14659,7 @@ status open \begin_layout Plain Layout -scala> def f2u(x: Int, z: Int): Int = z + x // Type signature (Int, Int) +scala> def f2u(x: Int, z: Int): Int = z + x // Type signature (Int, Int) => Int \end_layout @@ -14930,7 +14693,7 @@ f3 \emph on cannot \emph default - be converted to a non-higher-order function because + be converted to a first-order function because \begin_inset listings inline true status open @@ -15049,7 +14812,11 @@ A bound variable is invisible outside the scope that defines it. \begin_layout Standard However, outside code may define a variable that (by chance) has the same name as a bound variable inside the scope. - Consider this example from calculus: In the integral: + Consider an example from calculus where a function +\begin_inset Formula $f$ +\end_inset + + is defined via an integral: \begin_inset Formula \[ f(x)=\int_{0}^{x}\frac{dx}{1+x}\quad, @@ -15057,7 +14824,7 @@ f(x)=\int_{0}^{x}\frac{dx}{1+x}\quad, \end_inset - +Here, \emph on two \emph default @@ -15158,7 +14925,7 @@ Since this is the standard mathematical convention, the same convention \begin_layout Standard Name shadowing is not advisable in practical programming, because it usually decreases the clarity of code and so invites errors. - Consider the nameless function + Consider the nameless function: \begin_inset Formula \[ x\rightarrow x\rightarrow x\quad, @@ -15274,8 +15041,8 @@ operators \end_layout \begin_layout Standard -Many programming languages (such as ML, OCaml, F#, Haskell, Elm, PureScript) - have adopted this +Some programming languages (such as ML, OCaml, Haskell, and F#) have adopted + this \begin_inset Quotes eld \end_inset @@ -15574,9 +15341,8 @@ f (g 10) (h 20) + 30 \begin_layout Standard In Scala, the choice of whether to use curried or uncurried function signatures - is largely a matter of syntactic convenience. - Most Scala code tends to be written with uncurried functions. - Curried functions are used when they produce more easily readable code. + is mostly a matter of syntactic convenience. + Most Scala code seems to be written with uncurried functions. \end_layout \begin_layout Standard @@ -15630,21 +15396,6 @@ def summation2(a: Int, b: Int)(g: Int => Int): Int = (a to b).map(g).sum \end_inset These functions are applied to arguments like this: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "48col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -15675,20 +15426,6 @@ res1: Int = 3135 \end_inset - -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The code that calls \begin_inset listings inline true @@ -15884,8 +15621,9 @@ type inference status open \begin_layout Plain Layout - +See \family typewriter + \begin_inset CommandInset href LatexCommand href target "https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system" @@ -15902,8 +15640,9 @@ target "https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system" status open \begin_layout Plain Layout - +See \family typewriter + \begin_inset CommandInset href LatexCommand href target "http://dysphoria.net/2009/06/28/hindley-milner-type-inference-in-scala/" @@ -15969,44 +15708,7 @@ def pa[A, B, C](x: A)(f: (A, B) => C): B => C = ??? \end_inset -The function -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -pa -\end_layout - -\end_inset - - substitutes a fixed argument value -\begin_inset listings -inline true -status open -\begin_layout Plain Layout - -x:A -\end_layout - -\end_inset - - into another given function -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -f -\end_layout - -\end_inset - -. - \end_layout \begin_layout Standard @@ -16088,7 +15790,7 @@ status open \begin_layout Plain Layout -x:A +x: A \end_layout \end_inset @@ -16100,7 +15802,7 @@ status open \begin_layout Plain Layout -y:B +y: B \end_layout \end_inset @@ -16112,7 +15814,7 @@ status open \begin_layout Plain Layout -f:(A, B)=>C +f: (A, B) => C \end_layout \end_inset @@ -16322,7 +16024,7 @@ status open \begin_layout Plain Layout -x:A +x: A \end_layout \end_inset @@ -16334,7 +16036,7 @@ status open \begin_layout Plain Layout -y:B +y: B \end_layout \end_inset @@ -16524,7 +16226,7 @@ In the inner scope, we need to compute a value of type \end_layout \begin_layout Standard -We have unambiguously inferred the body of the function from its type signature: +We have derived the body of the function from its type signature: \begin_inset listings inline false status open diff --git a/sofp-src/sofp-higher-order-functions.tex b/sofp-src/sofp-higher-order-functions.tex index 7281bd808..95d807a66 100644 --- a/sofp-src/sofp-higher-order-functions.tex +++ b/sofp-src/sofp-higher-order-functions.tex @@ -85,7 +85,7 @@ \subsection{Motivation and first examples} As another example of the capture of values, consider this code: \begin{lstlisting} -val f: (Int => Int) = { +val f: (Int => Int) = { // Parentheses around (Int => Int) are optional. val p = 10 val q = 20 x => p + q * x @@ -93,10 +93,7 @@ \subsection{Motivation and first examples} \end{lstlisting} The body of the function \lstinline!f! is equivalent to \lstinline!{ x => 10 + 20 * x }! because the values \lstinline!p = 10! and \lstinline!q = 20! are -captured. - -The parentheses around a function\textsf{'}s type, such as \lstinline!(Int => Int)!, -are optional in Scala. +captured. \subsection{Curried and uncurried functions} @@ -166,7 +163,8 @@ \subsection{Curried and uncurried functions} def foldLeft[A, R](xs: Seq[A])(init: R)(update: (R, A) => R): R \end{lstlisting} The type signatures of these functions can be also written equivalently -as: +without argument names, although this is less convenient in practical +coding: \begin{lstlisting} def map[A, B]: Seq[A] => (A => B) => Seq[B] def fmap[A, B]: (A => B) => Option[A] => Option[B] @@ -232,14 +230,13 @@ \subsection{Equivalence of curried and uncurried functions} \begin{lstlisting} val f1: Int => Int => Int = { x => y => x - y } \end{lstlisting} - -Let us compare the function \lstinline!f1! with a function that takes -its two arguments at once, e.g.: +Let us rewrite \lstinline!f1! as a function that takes its two arguments +at once: \begin{lstlisting} def f2(x: Int, y: Int): Int = x - y \end{lstlisting} The function \lstinline!f2! has type signature \lstinline!(Int, Int) => Int!. -The syntax for using \lstinline!f1! and \lstinline!f2! is different: +Calling \lstinline!f1! and \lstinline!f2! requires different syntax: \begin{lstlisting} scala> f1(20)(4) res0: Int = 16 @@ -266,11 +263,8 @@ \subsection{Equivalence of curried and uncurried functions} A full application returns a value that is not of a function type. So, it cannot be applied to more arguments. -If we need to partially apply an \emph{uncurried} function, we can -use the underscore ($\_$) symbol: - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0.9\baselineskip} +To partially apply an \emph{uncurried} function, we can use the underscore +($\_$) symbol: \begin{lstlisting}[numbers=left] scala> val r2: Int => Int = f2(20, _) r2: Int => Int = @@ -278,18 +272,15 @@ \subsection{Equivalence of curried and uncurried functions} scala> r2(4) res3: Int = 16 \end{lstlisting} -\vspace{-0.95\baselineskip} -\end{wrapfigure}% - -\noindent (The type annotation \lstinline!Int => Int! is required -in line 1.) This code creates a function \lstinline!r2! by partially -applying \lstinline!f2! to the first argument but not to the second. -Other than that, \lstinline!r2! is the same function as \lstinline!r1! +(The type annotation \lstinline!Int => Int! is required in line 1.) +This code creates a function \lstinline!r2! by partially applying +\lstinline!f2! to the first argument but not to the second. Other +than that, \lstinline!r2! is the same function as \lstinline!r1! defined above; i.e., \lstinline!r2! returns the same values for the same arguments as \lstinline!r1!. A more verbose syntax for a partial application is: \begin{lstlisting} -scala> val r3: Int => Int = { x => f2(20, x) } // Same as r2 above. +scala> val r3: Int => Int = { x => f2(20, x) } // Same as r2 above. r3: Int => Int = scala> r3(4) @@ -326,9 +317,6 @@ \subsection{Equivalence of curried and uncurried functions} uncurried one, and vice versa. The Scala library defines the methods \lstinline!curried! and \lstinline!uncurried! that convert between these forms of functions. To convert between \lstinline!f2! and \lstinline!f1!: - -\begin{wrapfigure}{l}{0.42\columnwidth}% -\vspace{-0.9\baselineskip} \begin{lstlisting} scala> val f1c = (f2 _).curried f1c: Int => (Int => Int) = @@ -336,19 +324,16 @@ \subsection{Equivalence of curried and uncurried functions} scala> val f2u = Function.uncurried(f1c) f2u: (Int, Int) => Int = \end{lstlisting} -\vspace{-0.95\baselineskip} -\end{wrapfigure}% - -\noindent The syntax \lstinline!(f2 _)! is needed in Scala to convert -methods to function values. Recall that Scala has two ways of defining -a function: one as a method\index{Scala method} (defined using \lstinline!def!), +The syntax \lstinline!(f2 _)! is needed in Scala to convert methods +to function values. Recall that Scala has two ways of defining a function: +one as a method\index{Scala method} (defined using \lstinline!def!), another as a function value\index{function as a value} (defined using \lstinline!val!). The extra underscore will become unnecessary in Scala 3. The methods \lstinline!curried! and \lstinline!uncurried! are quick -to implement, as we will show in Section~\ref{subsec:Examples-of-fully-parametric}. -These functions are called the \textbf{currying}\index{currying} +to implement (see Section~\ref{subsec:Examples-of-fully-parametric} +below). These functions are called the \textbf{currying}\index{currying} and \textbf{uncurrying}\index{uncurrying} transformations. \section{Fully parametric functions\label{sec:Fully-parametric-functions}} @@ -412,22 +397,16 @@ \section{Fully parametric functions\label{sec:Fully-parametric-functions}} \end{lstlisting} We can introduce type parameters into the type signature of \lstinline!swap! to make it fully parametric: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.55\baselineskip} \begin{lstlisting} def swap[A, B](p: (A, B)): (B, A) = p match { case (x, y) => (y, x) } \end{lstlisting} -\vspace{-0.95\baselineskip} -\end{wrapfigure}% - -\noindent Converting \lstinline!swap! into a fully parametric function -is possible because the operation of swapping the parts of a tuple -\lstinline!(A, B)! works in the same way for all types \lstinline!A!, -\lstinline!B!. No changes were made in the body of the function. -The specialized version of \lstinline!swap! working on \lstinline!(Double, Double)! +Converting \lstinline!swap! into a fully parametric function is possible +because the operation of swapping the parts of a tuple \lstinline!(A, B)! +works in the same way for all types \lstinline!A!, \lstinline!B!. +No changes were made in the body of the function. The specialized +version of \lstinline!swap! working on \lstinline!(Double, Double)! can be obtained from the fully parametric version of \lstinline!swap! if we set the type parameters as \lstinline!A = Double!, \lstinline!B = Double!. @@ -467,7 +446,7 @@ \section{Fully parametric functions\label{sec:Fully-parametric-functions}} If needed, other swapping functions can be implemented for tuples with more elements, e.g.: \begin{lstlisting} -def swapAC[A,B,C]: ((A, B, C)) => (C, B, A) = { case (x, y, z) => (z, y, x) } +def swapAC[A, B, C]: ((A, B, C)) => (C, B, A) = { case (x, y, z) => (z, y, x) } \end{lstlisting} The Scala syntax requires \emph{double} parentheses around tuple types\index{tuples!as function arguments} of arguments but not around the tuple type of a function\textsf{'}s result. @@ -529,7 +508,8 @@ \subsection{Examples. Function composition\label{subsec:Examples-of-fully-parame as \lstinline!Int => String!. This book denotes the forward composition by the symbol ${\displaystyle \bef}$ -(pronounced \textsf{``}before\textsf{''}). We write: +(which can be read as \textsf{``}before\textsf{''}). We define $f\bef g$ (reads \textsf{``}$f$ +before $g$\textsf{''}) as: \begin{equation} f\bef g\triangleq x\rightarrow g(f(x))\quad.\label{eq:def-of-forward-composition} \end{equation} @@ -651,10 +631,11 @@ \subsection{Laws of function composition\label{subsec:Laws-of-function-compositi \[ \text{id}\bef f=\left(x\rightarrow f(\text{id}\,(x))\right)=\left(x\rightarrow f(x)\right)=f\quad. \] -The last step works since $x\rightarrow f(x)$ is a function taking -an argument $x$ and applying $f$ to that argument; i.e., $x\rightarrow f(x)$ -is an \textbf{expanded form}\index{expanded form of a function} of -the same function $f$. +The last step works since $x\rightarrow f(x)$ is a function that +takes an argument $x$ and applies $f$ to that argument. This is +the same function as $f$. We say that $x\rightarrow f(x)$ is an +\textbf{expanded form}\index{expanded form of a function} of the +same function $f$. We turn to the right identity law, $f\bef\text{id}=f$. Write out the left-hand side: @@ -773,8 +754,8 @@ \subsection{Laws of function composition\label{subsec:Laws-of-function-compositi more visually clear in the forward notation: it is harder to check that types match in Eq.~(\ref{eq:assoc-law-for-composition-with-types-backward}) than in Eq.~(\ref{eq:associativity-law-for-function-composition-with-types}). -To make the backward notation easier to work with, one could write\footnote{This is done in the book \textsf{``}Program design by calculation\textsf{''}, see -\texttt{\href{http://www4.di.uminho.pt/~jno/ps/pdbc.pdf}{http://www4.di.uminho.pt/$\sim$jno/ps/pdbc.pdf}}} the function types in reverse as, e.g., $g^{:C\leftarrow B}\circ f^{:B\leftarrow A}$. +To make the backward notation easier to work with, one could write\footnote{This is done in the book \textsf{``}Program design by calculation\textsf{''} by J.~N.~Oliveira +where the backward composition is used exclusively, see \texttt{\href{http://www4.di.uminho.pt/~jno/ps/pdbc.pdf}{http://www4.di.uminho.pt/$\sim$jno/ps/pdbc.pdf}}} the function types in reverse as, e.g., $g^{:C\leftarrow B}\circ f^{:B\leftarrow A}$. \subsection{Example: A function that is \emph{not} fully parametric} @@ -822,9 +803,6 @@ \subsection{Example: A function that is \emph{not} fully parametric} By the identity law, we should have $f_{2}=f_{1}\bef\text{id}=f_{1}$. But we can check that \lstinline!f_1! and \lstinline!f_2! are not equal: - -\begin{wrapfigure}{l}{0.17\columnwidth}% -\vspace{-0.9\baselineskip} \begin{lstlisting} scala> f_1(0) res3: Int = 1 @@ -833,11 +811,8 @@ \subsection{Example: A function that is \emph{not} fully parametric} res4: Int = 0 \end{lstlisting} -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent It is important that we are able to detect that \lstinline!fid! -is not a fully parametric function by checking whether some equation +It is important that we are able to detect that \lstinline!fid! is +not a fully parametric function by checking whether some equation holds, without looking at the code of \lstinline!fid!. In this book, we will always formulate any desired properties through equations or \textsf{``}laws\textsf{''}. To verify that a law holds, we will perform symbolic @@ -870,18 +845,12 @@ \subsection{Calculations with curried functions} To run this computation in Scala, we need to add a type annotation to the nameless function as in $(x^{:\text{Int}}\rightarrow x+10)(2)$. The code is: - -\begin{wrapfigure}{l}{0.311\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} scala> ((x: Int) => x + 10)(2) res0: Int = 12 \end{lstlisting} -\vspace{-1.3\baselineskip} -\end{wrapfigure}% - -\noindent Curried function calls such as $f(x)(y)$ or $\left(x\rightarrow\text{expr}(x)\right)(y)(z)$ +Curried function calls such as $f(x)(y)$ or $\left(x\rightarrow\text{expr}(x)\right)(y)(z)$ may look unfamiliar and confusing. We need to get some experience working with them. @@ -945,7 +914,7 @@ \subsection{Calculations with curried functions} Nameless functions are \emph{values} and can be used as part of larger expressions, just as any other values. For instance, nameless functions can be arguments of other functions (nameless or not). Here is an -example of applying a nameless function $f\rightarrow f(9)$ to a +example of applying a nameless function $f\rightarrow f(9)$ to the nameless function $x\rightarrow x\%\,4$: \begin{align*} & (f\rightarrow\gunderline f(9))\left(x\rightarrow x\%\,4\right)\\ @@ -961,28 +930,21 @@ \subsection{Calculations with curried functions} To verify this result in Scala, we need to specify a type annotation for $f$: - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} scala> ((f: Int => Int) => f(9))(x => x % 4) res2: Int = 1 \end{lstlisting} -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -\noindent No type annotation is needed for $x\rightarrow x\%\,4$ -since the Scala compiler already knows the type of $f$ and figures -out that $x$ in $x\rightarrow x\%\,4$ must have type \lstinline!Int!. +No type annotation is needed for $x\rightarrow x\%\,4$ because the +Scala compiler already knows the type of $f$ and figures out that +$x$ in $x\rightarrow x\%\,4$ must have type \lstinline!Int!. Let us summarize the syntax conventions for curried nameless functions: \begin{itemize} \item Function expressions group everything to the right: $x\rightarrow y\rightarrow z\rightarrow e$ means $x\rightarrow\left(y\rightarrow\left(z\rightarrow e\right)\right)$. -\item Function calls group everything to the left. So, $f(x)(y)(z)$ means -$\big((f(x))(y)\big)(z)$, i.e., $f(x)$ gives a new function that -is applied to $y$, giving again a new function that is finally applied -to $z$. +\item Function calls group everything to the left: $f(x)(y)(z)$ means $\big((f(x))(y)\big)(z)$. +The expression $f(x)$ is a new function that is applied to $y$, +giving again a new function that is finally applied to $z$. \item Function applications group stronger than infix operations, so $f(x)+y$ means $(f(x))+y$, as usual in mathematics, and not $f(x+y)$. \end{itemize} @@ -1050,21 +1012,15 @@ \subsection{Calculations with curried functions} But then $g(2)$ has type $\text{Int}$, and so we must have $A=\text{Int}$. Thus, the type of $f$ is $\left(\text{Int}\rightarrow\text{Int}\right)\rightarrow\text{Int}$. We know enough to write the Scala code now: - -\begin{wrapfigure}{l}{0.6885\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} scala> ((f: (Int => Int) => Int) => p => f(p))(g => g(2))(x => x + 4) res6: Int = 6 \end{lstlisting} -\vspace{-1\baselineskip} -\end{wrapfigure}% +Type annotations for $p$, $g$, and $x$ may be omitted: Scala\textsf{'}s +compiler can figure out the missing types from the given type of $f$. +However, extra type annotations often make code clearer. -\noindent Type annotations for $p$, $g$, and $x$ may be omitted: -Scala\textsf{'}s compiler will figure out the missing types from the given -type of $f$. However, extra type annotations often make code clearer. - -\subsection{Solved examples: Deriving a function\textsf{'}s type from its code\index{solved examples}} +\subsection{Examples: Deriving a function\textsf{'}s type from its code\index{examples (with code)}} Checking that the types match is an important part of the functional programming paradigm, both in the practice of writing code and in @@ -1180,7 +1136,6 @@ \subsubsection{Example \label{subsec:Example-hof-derive-types-2}\ref{subsec:Exam To transform \lstinline!twice! into a fully parametric function means replacing its type signature by a fully parameterized type signature while keeping the function body unchanged: - \begin{lstlisting} def twice[A, B, ...](f: ...): ... = { x => f(f(x)) } \end{lstlisting} @@ -1196,17 +1151,11 @@ \subsubsection{Example \label{subsec:Example-hof-derive-types-2}\ref{subsec:Exam only if $A=B$. In this way, $x^{:A}$ implies $f^{:A\rightarrow A}$, and the expression $x\rightarrow f(f(x))$ has type $A\rightarrow A$. We can now write the type signature of \lstinline!twice!: - -\begin{wrapfigure}{l}{0.51\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} def twice[A](f: A => A): A => A = { x => f(f(x)) } \end{lstlisting} -\vspace{-0.95\baselineskip} -\end{wrapfigure}% - -\noindent This fully parametric function can be written in the code -notation as: +This fully parametric function can be written in the code notation +as: \begin{equation} \text{twice}^{A}\triangleq f^{:A\rightarrow A}\rightarrow x^{:A}\rightarrow f(f(x))=f^{:A\rightarrow A}\rightarrow f\bef f\quad.\label{eq:hof-def-of-twice-in-math-notation} \end{equation} @@ -1355,7 +1304,7 @@ \section{Summary} \hline {\small{}$\text{id}^{A}$, also $\text{id}^{:A\rightarrow A}$} & {\small{}}\lstinline!identity[A]! & {\small{}the standard \textsf{``}identity\textsf{''} function}\tabularnewline \hline -{\small{}$A\rightarrow B\rightarrow C$} & {\small{}}\lstinline!A => B => C! & {\small{}type of a curried function}\tabularnewline +{\small{}$A\rightarrow B\rightarrow C$} & {\small{}}\lstinline!A => B => C! & {\small{}the type of a curried function}\tabularnewline \hline {\small{}$f\bef g$} & {\small{}}\lstinline!f andThen g! & {\small{}forward composition of functions}\tabularnewline \hline @@ -1370,17 +1319,15 @@ \section{Summary} What can we do using this chapter\textsf{'}s techniques? \begin{itemize} -\item Implement functions that return new functions and/or take functions -as arguments. -\item Simplify function expressions symbolically when functions are applied -to arguments. -\item Infer the most general type for a given code expression (perform type -inference). +\item Make functions that return new functions and/or take functions as +arguments. +\item Simplify expressions symbolically when functions are applied to arguments. +\item Derive a general type for a given code expression (perform type inference). \item Convert functions to a fully parametric form when possible. \end{itemize} The following examples and exercises illustrate these techniques. -\subsection{Solved examples\index{solved examples}} +\subsection{Examples\index{examples (with code)}} \subsubsection{Example \label{subsec:Example-hof-simple-1}\ref{subsec:Example-hof-simple-1}} @@ -1502,9 +1449,8 @@ \subsubsection{Example \label{subsec:Example-hof-simple-3}\ref{subsec:Example-ho \begin{lstlisting} val prime_f: (Int => Int) => Int => Boolean = ??? \end{lstlisting} -(The parentheses around \lstinline!Int => Int! are mandatory since -\lstinline!Int => Int => Int => Boolean! would be a completely different -type.) The implementation is: +(The parentheses around \lstinline!Int => Int! are mandatory as \lstinline!Int => Int => Int => Boolean! +would be a completely different type.) The implementation is: \begin{lstlisting} val prime_f: (Int => Int) => Int => Boolean = { f => x => isPrime(f(x)) } \end{lstlisting} @@ -1573,7 +1519,7 @@ \subsubsection{Example \label{subsec:Example-hof-derive-types-5}\ref{subsec:Exam \subparagraph{Solution} -Begin by assuming $f^{:A}$ with a type parameter $A$. In the sub-expression +To begin, assume $f^{:A}$ with a type parameter $A$. In the sub-expression $g\rightarrow g(f)$, the curried argument $g$ must itself be a function, because it is being applied to $f$ as $g(f)$. So, we assign types as $f^{:A}\rightarrow g^{:A\rightarrow B}\rightarrow g(f)$, where @@ -1594,14 +1540,15 @@ \subsubsection{Example \label{subsec:Example-hof-derive-types-5}\ref{subsec:Exam The type of the first curried argument of \lstinline!q[A, B]!, which is $A$, must match the entire type of \lstinline!q[C, D]!, which is $C\rightarrow\left(C\rightarrow D\right)\rightarrow D$. So, we -must set the type parameter $A$ as: +must choose $A$ as: \[ A=C\rightarrow\left(C\rightarrow D\right)\rightarrow D\quad. \] The type of \lstinline!q(q)! becomes: -\[ -q^{A,B}(q^{C,D}):\left(\left(C\rightarrow\left(C\rightarrow D\right)\rightarrow D\right)\rightarrow B\right)\rightarrow B\quad,\quad\text{where}\quad A=C\rightarrow\left(C\rightarrow D\right)\rightarrow D\quad. -\] +\begin{align*} +q^{A,B}(q^{C,D}) & :\left(\left(C\rightarrow\left(C\rightarrow D\right)\rightarrow D\right)\rightarrow B\right)\rightarrow B\quad,\\ + & \quad\text{where}\quad A=C\rightarrow\left(C\rightarrow D\right)\rightarrow D\quad. +\end{align*} There are no other constraints on the type parameters $B$, $C$, $D$. @@ -1706,15 +1653,16 @@ \subsubsection{Example \label{subsec:Example-hof-curried}\ref{subsec:Example-hof Having established that types match, we can now omit the type annotations and rewrite the code: \begin{align*} - & (f\rightarrow g\rightarrow\gunderline g(\gunderline f))\left(x\rightarrow y\rightarrow y(x)\right)\left(h\rightarrow h(10)\right)\\ -{\color{greenunder}\text{substitute }f=x\rightarrow y\rightarrow y(x)\text{ and }g=h\rightarrow h(10):}\quad & =(h\rightarrow\gunderline h(10))\left(x\rightarrow y\rightarrow y(x)\right)\\ -{\color{greenunder}\text{substitute }h:}\quad & =(x\rightarrow y\rightarrow y(\gunderline x))(10)\\ -{\color{greenunder}\text{substitute }x:}\quad & =y\rightarrow y(10)\quad. + & (\gunderline f\rightarrow g\rightarrow g(\gunderline f))\gunderline{\left(x\rightarrow y\rightarrow y(x)\right)}\left(h\rightarrow h(10)\right)\\ +{\color{greenunder}\text{substitute }f=x\rightarrow y\rightarrow y(x):}\quad & =\big(\gunderline g\rightarrow\gunderline g(x\rightarrow y\rightarrow y(x))\big)\gunderline{\left(h\rightarrow h(10)\right)}\\ +{\color{greenunder}\text{substitute }g=h\rightarrow h(10):}\quad & =(\gunderline h\rightarrow\gunderline h(10))\gunderline{\left(x\rightarrow y\rightarrow y(x)\right)}\\ +{\color{greenunder}\text{substitute }h=x\rightarrow y\rightarrow y(x):}\quad & =(\gunderline x\rightarrow y\rightarrow y(\gunderline x))\gunderline{(10)}\\ +{\color{greenunder}\text{substitute }x=10:}\quad & =y\rightarrow y(10)\quad. \end{align*} The type of this expression is $\left(\text{Int}\rightarrow D\right)\rightarrow D$ with a type parameter $D$. Since the argument $y$ is an arbitrary -function, we cannot simplify $y(10)$ or $y\rightarrow y(10)$ any -further. We conclude that the final simplified form of Eq.~(\ref{eq:example-hof-curried-function-solved1}) +function, we cannot simplify either $y(10)$ or $y\rightarrow y(10)$ +any further. We conclude that the final simplified form of Eq.~(\ref{eq:example-hof-curried-function-solved1}) is $y^{:\text{Int}\rightarrow D}\rightarrow y(10)$. To test this, we first define the function $f\rightarrow g\rightarrow g(f)$ @@ -1736,7 +1684,7 @@ \subsubsection{Example \label{subsec:Example-hof-curried}\ref{subsec:Example-hof \end{lstlisting} To verify that the function $s^{D}$ indeed equals $y^{:\text{Int}\rightarrow D}\rightarrow y(10)$, we apply $s^{D}$ to some functions of type $\text{Int}\rightarrow D$, -say, for $D=\text{Boolean}$ or $D=\text{Int}$: +say, with $D=\text{Boolean}$ or $D=\text{Int}$: \begin{lstlisting} scala> s(_ > 0) // Set D = Boolean and evaluate (10 > 0). res6: Boolean = true @@ -1763,12 +1711,18 @@ \subsubsection{Example \label{subsec:Example-hof-composition}\ref{subsec:Example & \left(x\rightarrow y\rightarrow x(x(y))\right)\bef(p\rightarrow p(2))=x\rightarrow(y\rightarrow x(x(y)))(2)=x\rightarrow x(x(2))\quad.\\ & \left(x\rightarrow x(x(2))\right)\bef\left(z\rightarrow z+3\right)=x\rightarrow x(x(2))+3\quad. \end{align*} -Computing the pairwise combinations in another order, we get: +Computing the pairwise combinations in another order, we get the same +result: \begin{align*} & \left(p\rightarrow p(2)\right)\bef\left(z\rightarrow z+3\right)=p\rightarrow p(2)+3\quad.\\ - & \left(x\rightarrow y\rightarrow x(x(y))\right)\bef\left(p\rightarrow p(2)+3\right)=x\rightarrow\left(y\rightarrow x(x(y))\right)(2)+3=x\rightarrow x(x(2))+3\quad. + & \left(x\rightarrow y\rightarrow x(x(y))\right)\bef\left(p\rightarrow p(2)+3\right)=x\rightarrow\left(y\rightarrow x(x(y))\right)(2)+3\\ + & \quad=x\rightarrow x(x(2))+3\quad. \end{align*} -Types are inferred as: $\left(x\rightarrow y\rightarrow x(x(y))\right)^{:(\text{Int}\rightarrow\text{Int})\rightarrow(\text{Int}\rightarrow\text{Int})}\bef\left(p\rightarrow p(2)\right)^{:(\text{Int}\rightarrow\text{Int})\rightarrow\text{Int}}\bef\left(z\rightarrow z+3\right)^{:\text{Int}\rightarrow\text{Int}}$. +Types are inferred as: +\[ +\left(x\rightarrow y\rightarrow x(x(y))\right)^{:(\text{Int}\rightarrow\text{Int})\rightarrow(\text{Int}\rightarrow\text{Int})}\bef\left(p\rightarrow p(2)\right)^{:(\text{Int}\rightarrow\text{Int})\rightarrow\text{Int}}\bef\left(z\rightarrow z+3\right)^{:\text{Int}\rightarrow\text{Int}}\quad. +\] + \subsubsection{Example \label{subsec:Example-hof-const-function}\ref{subsec:Example-hof-const-function}} @@ -1794,9 +1748,9 @@ \subsection{Exercises\index{exercises}} \subsubsection{Exercise \label{subsec:Exercise-hof-simple-7}\ref{subsec:Exercise-hof-simple-7}} Revise the function from Exercise~\ref{subsec:ch1-transf-Exercise-4}, -implementing it as a curried function and replacing the hard-coded -number $100$ by a \emph{curried} first argument. The type signature -should become \texttt{}\lstinline!Int => List[List[Int]] => List[List[Int]]!\texttt{.} +making it a curried function and replacing the hard-coded number $100$ +by a \emph{curried} first argument. The type signature should become +\texttt{}\lstinline!Int => List[List[Int]] => List[List[Int]]!\texttt{.} \subsubsection{Exercise \label{subsec:Exercise-hof-simple-8}\ref{subsec:Exercise-hof-simple-8}} @@ -1958,7 +1912,7 @@ \subsection{Higher-order functions} and \lstinline!uncurried! are examples of higher-order functions that take other functions as arguments \emph{and} return new functions. -The following examples illustrate the concept of a function\textsf{'}s order. +The following examples illustrate the concept of a function\textsf{'}s \textsf{``}order\textsf{''}. Consider the code: \begin{lstlisting} def f1(x: Int): Int = x + 10 @@ -1980,12 +1934,12 @@ \subsection{Higher-order functions} because its return value is of a function type. An equivalent computation can be performed by an uncurried function that is \emph{not} higher-order: \begin{lstlisting} -scala> def f2u(x: Int, z: Int): Int = z + x // Type signature (Int, Int) => Int +scala> def f2u(x: Int, z: Int): Int = z + x // Type signature (Int, Int) => Int \end{lstlisting} Unlike \lstinline!f2!, the function \lstinline!f3! \emph{cannot} -be converted to a non-higher-order function because \lstinline!f3! -has an argument of a function type. Converting to an uncurried form -cannot eliminate such arguments. +be converted to a first-order function because \lstinline!f3! has +an argument of a function type. Converting to an uncurried form cannot +eliminate such arguments. \subsection{Name shadowing and the scope of bound variables} @@ -2010,13 +1964,13 @@ \subsection{Name shadowing and the scope of bound variables} use it and depend on its value. However, outside code may define a variable that (by chance) has the -same name as a bound variable inside the scope. Consider this example -from calculus: In the integral: +same name as a bound variable inside the scope. Consider an example +from calculus where a function $f$ is defined via an integral: \[ f(x)=\int_{0}^{x}\frac{dx}{1+x}\quad, \] -\emph{two} bound variables named $x$ are defined in two scopes: one -in the scope of $f$, another in the scope of the nameless function +Here, \emph{two} bound variables named $x$ are defined in two scopes: +one in the scope of $f$, another in the scope of the nameless function $x\rightarrow\frac{1}{1+x}$. The convention in mathematics is to treat these two $x$\textsf{'}s as two \emph{completely} \emph{different} variables that just happen to have the same name. In sub-expressions where both @@ -2039,7 +1993,7 @@ \subsection{Name shadowing and the scope of bound variables} Name shadowing is not advisable in practical programming, because it usually decreases the clarity of code and so invites errors. Consider -the nameless function +the nameless function: \[ x\rightarrow x\rightarrow x\quad, \] @@ -2067,10 +2021,10 @@ \subsection{Operator syntax for function applications} that are written without parentheses, similar to the operators of summation, $\sum_{k}f(k)$, or differentiation, $\frac{d}{dx}f(x)$. -Many programming languages (such as ML, OCaml, F\#, Haskell, Elm, -PureScript) have adopted this \textsf{``}operator syntax\index{operator syntax}\textsf{''}, -making parentheses optional for function arguments. In those languages, -\lstinline!f x! means the same as \lstinline!f(x)!.\footnote{The operator syntax has a long history in programming. It is used +Some programming languages (such as ML, OCaml, Haskell, and F\#) have +adopted this \textsf{``}operator syntax\index{operator syntax}\textsf{''}, making +parentheses optional for function arguments. In those languages, \lstinline!f x! +means the same as \lstinline!f(x)!.\footnote{The operator syntax has a long history in programming. It is used in Unix shell commands, for example \lstinline!cp file1 file2!. In LISP-like languages, function applications are enclosed in parentheses but the arguments are space-separated, for example \lstinline!(f 10 20)!. @@ -2104,9 +2058,8 @@ \subsection{Operator syntax for function applications} for complicated expressions and then use those names as curried arguments. In Scala, the choice of whether to use curried or uncurried function -signatures is largely a matter of syntactic convenience. Most Scala -code tends to be written with uncurried functions. Curried functions -are used when they produce more easily readable code. +signatures is mostly a matter of syntactic convenience. Most Scala +code seems to be written with uncurried functions. One of the syntactic features of Scala is the ability to give a curried argument using the curly brace syntax. Compare the two definitions @@ -2117,9 +2070,6 @@ \subsection{Operator syntax for function applications} def summation2(a: Int, b: Int)(g: Int => Int): Int = (a to b).map(g).sum \end{lstlisting} These functions are applied to arguments like this: - -\begin{wrapfigure}{l}{0.48\columnwidth}% -\vspace{-0.5\baselineskip} \begin{lstlisting} scala> summation1(1, 10, { x => x*x*x + 2*x }) res0: Int = 3135 @@ -2127,16 +2077,12 @@ \subsection{Operator syntax for function applications} scala> summation2(1, 10) { x => x*x*x + 2*x } res1: Int = 3135 \end{lstlisting} -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -\noindent The code that calls \lstinline!summation2! is easier to -read because the curried argument is syntactically separated from -the rest of the code by curly braces. This is especially useful when -the curried argument is itself a function with a complicated body, -since Scala\textsf{'}s curly braces syntax allows function bodies to contain -local definitions (\lstinline!val! or \lstinline!def!) of new bound -variables. +The code that calls \lstinline!summation2! is easier to read because +the curried argument is syntactically separated from the rest of the +code by curly braces. This is especially useful when the curried argument +is itself a function with a complicated body, since Scala\textsf{'}s curly +braces syntax allows function bodies to contain local definitions +(\lstinline!val! or \lstinline!def!) of new bound variables. Another feature of Scala is the \textsf{``}dotless\textsf{''} method syntax: for example, \lstinline!xs map f! is equivalent to \lstinline!xs.map(f)! and @@ -2151,8 +2097,8 @@ \subsection{Deriving a function\textsf{'}s code from its type\label{subsec:Deriv We have seen how the procedure of type inference\index{type inference} derives the type signature from a function\textsf{'}s code. A well-known algorithm -for type inference is the Damas-Hindley-Milner algorithm,\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Hindley\%E2\%80\%93Milner_type_system}{https://en.wikipedia.org/wiki/Hindley\%E2\%80\%93Milner\_type\_system}}} -with a Scala implementation available.\footnote{\texttt{\href{http://dysphoria.net/2009/06/28/hindley-milner-type-inference-in-scala/}{http://dysphoria.net/2009/06/28/hindley-milner-type-inference-in-scala/}}} +for type inference is the Damas-Hindley-Milner algorithm,\footnote{See \texttt{\href{https://en.wikipedia.org/wiki/Hindley\%E2\%80\%93Milner_type_system}{https://en.wikipedia.org/wiki/Hindley\%E2\%80\%93Milner\_type\_system}}} +with a Scala implementation available.\footnote{See \texttt{\href{http://dysphoria.net/2009/06/28/hindley-milner-type-inference-in-scala/}{http://dysphoria.net/2009/06/28/hindley-milner-type-inference-in-scala/}}} It is remarkable that one can sometimes perform \textsf{``}\index{code inference}code inference\textsf{''}: derive a function\textsf{'}s \emph{code} from the function\textsf{'}s @@ -2163,8 +2109,6 @@ \subsection{Deriving a function\textsf{'}s code from its type\label{subsec:Deriv \begin{lstlisting} def pa[A, B, C](x: A)(f: (A, B) => C): B => C = ??? \end{lstlisting} -The function \lstinline!pa! substitutes a fixed argument value \lstinline!x:A! -into another given function \lstinline!f!. How can we implement \lstinline!pa!? Since \lstinline!pa(x)(f)! must return a function of type \lstinline!B => C!, we have no choice @@ -2175,7 +2119,7 @@ \subsection{Deriving a function\textsf{'}s code from its type\label{subsec:Deriv } \end{lstlisting} In the inner scope, we need to compute a value of type \lstinline!C!, -and we have values \lstinline!x:A!, \lstinline!y:B!, and \lstinline!f:(A, B)=>C!. +and we have values \lstinline!x: A!, \lstinline!y: B!, and \lstinline!f: (A, B) => C!. How can we compute a value of type \lstinline!C!? If we knew that \lstinline!C = Int! when \lstinline!pa(x)(f)! is applied, we could have simply selected a fixed integer value, say, \lstinline!1!, as @@ -2189,7 +2133,7 @@ \subsection{Deriving a function\textsf{'}s code from its type\label{subsec:Deriv of type \lstinline!C! is by applying the function \lstinline!f! to arguments of types \lstinline!A! and \lstinline!B!. Since the types \lstinline!A! and \lstinline!B! are arbitrary, we cannot obtain -any values of these types other than \lstinline!x:A! and \lstinline!y:B!. +any values of these types other than \lstinline!x: A! and \lstinline!y: B!. So, the only way of getting a value of type \lstinline!C! is to compute \lstinline!f(x, y)!. Thus, the body of \lstinline!pa! must be: \begin{lstlisting} @@ -2220,8 +2164,7 @@ \subsection{Deriving a function\textsf{'}s code from its type\label{subsec:Deriv $A$, namely $x^{:A}$. So, the only way of obtaining the required result is to compute $g(f(x))$. -We have unambiguously inferred the body of the function from its type -signature: +We have derived the body of the function from its type signature: \begin{lstlisting} def before[A, B, C](f: A => B, g: B => C): A => C = { x => g(f(x)) } \end{lstlisting} diff --git a/sofp-src/sofp-induction.lyx b/sofp-src/sofp-induction.lyx index 83760fcc3..122f8f139 100644 --- a/sofp-src/sofp-induction.lyx +++ b/sofp-src/sofp-induction.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -385,7 +400,7 @@ Pairs and triples are examples of tuples. \series bold tuple \series default - can contain any number of values, which may be called + can contain several values called \series bold parts \begin_inset Index idx @@ -399,7 +414,7 @@ tuples!parts \series default - of a tuple (they are also called + or \series bold fields \series default @@ -413,10 +428,9 @@ tuples!fields \end_inset - a tuple). - The parts of a tuple can have different types, but the type of each part - is fixed once and for all. - Also, the number of parts in a tuple is fixed. + a tuple. + A tuple's parts can have different types, but the type of each part (and + the number of parts) is fixed once and for all. It is a \series bold type error @@ -971,18 +985,21 @@ case $pattern$ => ... \end_layout \begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "29.5col%" +Here is an example of a +\series bold +destructuring +\begin_inset Index idx status open \begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset +destructuring +\end_layout +\end_inset + definition +\series default +: \begin_inset listings inline false status open @@ -1023,35 +1040,7 @@ z: Int = 3 \end_inset - -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -An example of a -\series bold -destructuring -\begin_inset Index idx -status open - -\begin_layout Plain Layout -destructuring -\end_layout - -\end_inset - - definition -\series default - is shown at left. - The value +The value \begin_inset listings inline true status open @@ -1349,31 +1338,7 @@ pattern matching \begin_layout Standard Pattern matching is often used for working with tuples. - The expression -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -{case (a, b, c) => ...} -\end_layout - -\end_inset - - -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "60col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -70baselineskip% -\end_inset - - + Look at this example: \begin_inset listings inline false status open @@ -1390,16 +1355,19 @@ res0: Int = 6 \end_inset +The expression +\begin_inset listings +inline true +status open -\begin_inset VSpace -150baselineskip% -\end_inset - +\begin_layout Plain Layout +{case (a, b, c) => ...} \end_layout \end_inset -called a + called a \series bold case expression \series default @@ -1417,7 +1385,8 @@ case \end_inset - (shown at left) performs pattern matching on its argument. +. + It performs pattern matching on its argument. The pattern matching will \begin_inset Quotes eld \end_inset @@ -1594,26 +1563,6 @@ p \end_inset is destructured by pattern matching: -\begin_inset space \hfill{} -\end_inset - - -\begin_inset space ~ -\end_inset - - -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "52col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1649,22 +1598,6 @@ res0: String = result is 12 \end_inset - -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\series bold - -\end_layout - -\begin_layout Standard -\noindent The type structure of the argument \begin_inset listings inline true @@ -1835,26 +1768,6 @@ t2 In contrast, the code is changed easily when using the pattern matching expression instead of the accessor methods. We only need to change the type and the pattern: -\begin_inset space \hfill{} -\end_inset - - -\begin_inset space ~ -\end_inset - - -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "52col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1876,20 +1789,6 @@ def t4(p: ((Int, String), Int)): String = p match { \end_inset - -\begin_inset VSpace -60baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent It is easy to see that \begin_inset listings inline true @@ -1922,21 +1821,6 @@ t1 \begin_layout Standard Sometimes we only need to use certain parts of a tuple in a pattern match. The following syntax is used to make that clear: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "52col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1958,20 +1842,6 @@ z: Boolean = true \end_inset - -\begin_inset VSpace -60baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The underscore symbol ( \begin_inset listings inline true @@ -2118,6 +1988,20 @@ status open val q: (Int => Int, Int => Int) = (x => x + 1, x => x - 1) \end_layout +\begin_layout Plain Layout + +\end_layout + +\begin_layout Plain Layout + +scala> q._1(3) +\end_layout + +\begin_layout Plain Layout + +res0: Int = 4 +\end_layout + \end_inset We can create a list of tuples: @@ -2209,7 +2093,7 @@ scala> basket.map { case (fruit, count) => count * 2 } \begin_layout Plain Layout -res0: List[Int] = List(6, 4, 0) +res1: List[Int] = List(6, 4, 0) \end_layout \begin_layout Plain Layout @@ -2223,7 +2107,7 @@ scala> basket.map { case (fruit, count) => count * 2 }.sum \begin_layout Plain Layout -res1: Int = 10 +res2: Int = 10 \end_layout \end_inset @@ -2349,7 +2233,7 @@ scala> basket.map { case (fruit, count) => \begin_layout Plain Layout -res2: List[(String, Boolean)] = List((apples,false), (pears,false), (lemons,true +res3: List[(String, Boolean)] = List((apples,false), (pears,false), (lemons,true )) \end_layout @@ -2588,18 +2472,6 @@ Vector \end_inset -, -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Stream -\end_layout - -\end_inset - , and \begin_inset listings inline true @@ -3440,15 +3312,31 @@ flatten \end_layout \begin_layout Standard -converts nested sequences to -\begin_inset Quotes eld +converts a nested sequence type, such as +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +List[List[A]] +\end_layout + \end_inset -flattened -\begin_inset Quotes erd +, into a simple +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +List[A] +\end_layout + \end_inset - ones: + by concatenating all inner sequences into one: \begin_inset listings inline false status open @@ -3465,16 +3353,8 @@ res6: List[Int] = List(1, 2, 2, 3, 3, 4) \end_inset -Here, -\begin_inset Quotes eld -\end_inset - -flattening -\begin_inset Quotes erd -\end_inset - - means the concatenation of the inner sequences. - In Scala, sequences are concatenated using the operation +In Scala, sequences and other collections (such as sets and dictionaries) + are generally concatenated using the operation \begin_inset listings inline true status open @@ -3659,7 +3539,7 @@ status open \begin_layout Plain Layout -scala> List(1,2,3,4).map(n => (1 to n).toList) +scala> List(1, 2, 3, 4).map(n => (1 to n).toList) \end_layout \begin_layout Plain Layout @@ -3674,7 +3554,7 @@ res9: List[List[Int]] = List(List(1), List(1, 2), List(1, 2, 3), List(1, \begin_layout Plain Layout -scala> List(1,2,3,4).map(n => (1 to n).toList).flatten +scala> List(1, 2, 3, 4).map(n => (1 to n).toList).flatten \end_layout \begin_layout Plain Layout @@ -3688,7 +3568,7 @@ res10: List[Int] = List(1, 1, 2, 1, 2, 3, 1, 2, 3, 4) \begin_layout Plain Layout -scala> List(1,2,3,4).flatMap(n => (1 to n).toList) +scala> List(1, 2, 3, 4).flatMap(n => (1 to n).toList) \end_layout \begin_layout Plain Layout @@ -3865,14 +3745,6 @@ scala> Seq( \begin_inset Quotes eld \end_inset -wombat -\begin_inset Quotes erd -\end_inset - -, -\begin_inset Quotes eld -\end_inset - xenon \begin_inset Quotes erd \end_inset @@ -3906,8 +3778,7 @@ y \begin_layout Plain Layout -res12: Map[Int,Seq[String]] = Map(1 -> List(yogurt), 2 -> List(wombat, xenon, - zebra)) +res12: Map[Int,Seq[String]] = Map(1 -> List(yogurt), 2 -> List(xenon, zebra)) \end_layout \end_inset @@ -4278,12 +4149,12 @@ res3: Seq[String] = List(xx, yyy, z) \end_layout \begin_layout Subsection -Solved examples: Tuples and collections +Examples: Tuples and collections \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -4317,8 +4188,8 @@ For a given sequence \begin_inset Formula $x_{i}$ \end_inset -, compute the sequence of pairs -\begin_inset Formula $b_{i}=\left(\cos x_{i},\sin x_{i}\right)$ +, compute the sequence of pairs of values +\begin_inset Formula $\left(\cos x_{i},\sin x_{i}\right)$ \end_inset . @@ -4344,7 +4215,7 @@ status open \begin_layout Plain Layout -xs:Seq[Double] +xs: Seq[Double] \end_layout \end_inset @@ -4440,7 +4311,7 @@ status open \begin_layout Plain Layout -xs:Seq[Double] +xs: Seq[Double] \end_layout \end_inset @@ -4558,7 +4429,19 @@ For given sequences \begin_inset Formula $b_{i}$ \end_inset -, compute the sequence of differences + of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Double +\end_layout + +\end_inset + + values, compute the sequence of differences \begin_inset Formula $c_{i}=a_{i}-b_{i}$ \end_inset @@ -4614,19 +4497,7 @@ bs \end_inset - are of type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Seq[Double] -\end_layout - -\end_inset - - and have equal length. + have equal length. \end_layout \begin_layout Subparagraph @@ -4786,7 +4657,7 @@ status open \begin_layout Plain Layout -ps:Seq[Double] +ps: Seq[Double] \end_layout \end_inset @@ -4888,21 +4759,6 @@ ps.tail \end_inset , we get a sequence of pairs: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "53col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -4919,20 +4775,6 @@ res1: Seq[(Int, Int)] = List((1,2), (2,3), (3,4)) \end_inset - -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Note that \begin_inset listings inline true @@ -5177,41 +5019,65 @@ status open \begin_layout Plain Layout -nested => nested.max +x => x.max \end_layout \end_inset -: -\end_layout - -\begin_layout Standard + (where \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def c(b: List[Int], k: Int) = b.sliding(2 * k + 1).toList.map(nested => nested.max) +x \end_layout \end_inset -In Scala, this code can be written more concisely using the syntax: + will have type \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def c(b: List[Int], k: Int) = b.sliding(2 * k + 1).toList.map(_.max) +List[Int \end_layout \end_inset -because -\begin_inset listings -inline true +): +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def c(b: List[Int], k: Int) = b.sliding(2 * k + 1).toList.map(x => x.max) +\end_layout + +\end_inset + +This code can be written more concisely using the syntax: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def c(b: List[Int], k: Int) = b.sliding(2 * k + 1).toList.map(_.max) +\end_layout + +\end_inset + +because, in Scala, +\begin_inset listings +inline true status open \begin_layout Plain Layout @@ -5221,7 +5087,7 @@ _.max \end_inset - means the nameless function + is the same as the nameless function \begin_inset listings inline true status open @@ -5234,7 +5100,7 @@ x => x.max \end_inset . - Let us test this code: + Test this: \begin_inset listings inline false status open @@ -5281,7 +5147,7 @@ Create a \begin_inset Formula $10\times10$ \end_inset - multiplication table as a dictionary of type + multiplication table as a dictionary having the type \begin_inset listings inline true status open @@ -5496,21 +5362,6 @@ y => (1, y) \end_inset that converts a number into a tuple: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "53col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -5527,20 +5378,6 @@ res0: List[(Int, Int)] = List((1,1), (1,2), (1,3)) \end_inset - -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The curly braces in \begin_inset listings inline true @@ -6255,7 +6092,20 @@ status open \end_inset - pairs: + pairs. + The result is converted to a +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Map +\end_layout + +\end_inset + + automatically. \end_layout \begin_layout Standard @@ -6265,8 +6115,8 @@ status open \begin_layout Plain Layout -dict.map { case (name, addr) => (addr, name) } // The result is converted - to Map automatically. +dict.map { case (name, addr) => (addr, name) } // This has type Map[String, + String]. \end_layout \end_inset @@ -7161,7 +7011,7 @@ sortBy \end_inset operation is applied to that sequence. - So the sequence element matched by + So, the sequence element matched by \begin_inset listings inline true status open @@ -7173,7 +7023,7 @@ status open \end_inset - is a tuple + is a tuple having the type \begin_inset listings inline true status open @@ -7185,7 +7035,8 @@ status open \end_inset -, which means that the pattern variables +. + Then the pattern variables \begin_inset listings inline true status open @@ -7234,7 +7085,11 @@ Seq[String] \end_inset respectively. - It would be a type error to use the sorting key function +\begin_inset Note Note +status open + +\begin_layout Plain Layout +It would be a type error to use the sorting key function \begin_inset listings inline true status open @@ -7270,7 +7125,12 @@ words \end_inset - (because sorting by string sequences is not automatically defined). + (because string sequences do not have an automatically defined ordering). +\end_layout + +\end_inset + + \end_layout \begin_layout Standard @@ -7284,36 +7144,28 @@ status open \begin_layout Plain Layout -words: Seq[String] // Need to group by word length. - The result of groupBy() has type: -\end_layout - -\begin_layout Plain Layout - - Map[Int, Seq[String]] // Need to sort by word length; cannot sort - a dictionary! +words: Seq[String] // After groupBy() by word length, will have + type: \end_layout \begin_layout Plain Layout - // Need to convert this dictionary to a sequence: +Map[Int, Seq[String]] // To sort by word length, convert to a sequence: \end_layout \begin_layout Plain Layout - Seq[ (Int, Seq[String]) ] // Now sort this by the `Int` value. - Sorting does not change the types. +Seq[ (Int, Seq[String]) ] // Sort by the `Int` value; type is unchanged: \end_layout \begin_layout Plain Layout - // It remains to swap the parts of all tuples - in the sequence: +Seq[ (Int, Seq[String]) ] // It remains to swap the parts of the tuples: \end_layout \begin_layout Plain Layout - Seq[ (Seq[String], Int) ] // We are done. +Seq[ (Seq[String], Int) ] // We are done. \end_layout \end_inset @@ -7378,15 +7230,19 @@ noprefix "false" \end_layout \begin_layout Standard -Find all pairs +Find all integer pairs \begin_inset Formula $i,j$ \end_inset - within -\begin_inset Formula $\left(0,1,...,9\right)$ + where +\begin_inset Formula $0\leq i\leq9$ \end_inset - such that + and +\begin_inset Formula $0\leq j\leq9$ +\end_inset + + and \begin_inset Formula $i+4*j>i*j$ \end_inset @@ -7443,25 +7299,23 @@ noprefix "false" \end_layout \begin_layout Standard -Same task as in Exercise -\begin_inset space ~ +Find all integer triples +\begin_inset Formula $i,j,k$ \end_inset + where +\begin_inset Formula $0\leq i\leq9$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "tuples-Exercise-1" -plural "false" -caps "false" -noprefix "false" - +, +\begin_inset Formula $0\leq j\leq9$ \end_inset -, but for -\begin_inset Formula $i,j,k$ +, +\begin_inset Formula $0\leq k\leq9$ \end_inset - and the condition +, and \begin_inset Formula $i+4*j+9*k>i*j*k$ \end_inset @@ -7688,19 +7542,19 @@ Int \end_inset value is followed by a larger value. - For example, the input sequence + For example, the input \begin_inset listings inline true status open \begin_layout Plain Layout -Seq(1,3,2,4) +Seq(1, 3, 2, 4) \end_layout \end_inset - is to be converted into + must be converted into \begin_inset listings inline true status open @@ -7713,7 +7567,20 @@ Seq((1,true),(3,false),(2,true),(4,false)) \end_inset . - (The last element, 4, has no following element.) + The last value (here, 4) has no following value and is always paired with + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +false +\end_layout + +\end_inset + +. \end_layout \begin_layout Subsubsection @@ -7947,7 +7814,7 @@ String \end_inset . - The required type signature and a sample test: + The type signature and a sample test: \begin_inset listings inline false status open @@ -7972,8 +7839,7 @@ not yet implemented \begin_layout Plain Layout -scala> reorder(Seq(6.0,2.0,8.0,4.0), Seq(20,10,40,30)) // Test with type parameter - A = Double. +scala> reorder(Seq(6.0,2.0,8.0,4.0), Seq(20,10,40,30)) // Test with A = Double. \end_layout \begin_layout Plain Layout @@ -8154,7 +8020,7 @@ status open \begin_layout Plain Layout -String +Int \end_layout \end_inset @@ -8166,20 +8032,44 @@ status open \begin_layout Plain Layout -Int +String \end_layout \end_inset . - The return type of the function should be + The function's arguments should be of types +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Seq[Q] +\end_layout + +\end_inset + + and +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Seq[P] +\end_layout + +\end_inset + +, and the return type should be \begin_inset listings inline true status open \begin_layout Plain Layout -Map[Q, P] +Map[P, Q] \end_layout \end_inset @@ -8312,7 +8202,7 @@ apple \end_inset -the output must be +the output must be: \begin_inset listings inline true status open @@ -8420,7 +8310,7 @@ status open \begin_layout Plain Layout -Seq[List[Int]] +Seq[Seq[Int]] \end_layout \end_inset @@ -8432,20 +8322,46 @@ status open \begin_layout Plain Layout -Seq[List[Int]] +Seq[Seq[Int]] \end_layout \end_inset - where each new inner list contains + where each new inner sequence contains the \begin_inset Formula $3$ \end_inset - largest elements from the old inner list (or fewer than + largest elements from the corresponding old inner sequence, sorted in descendin +g order (or fewer than \begin_inset Formula $3$ \end_inset - if the old inner list is shorter). + elements if the old inner sequence is shorter). + So, for the input: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +Seq(Seq(0, 50, 5, 10, 30), Seq(10, 100), Seq(1, 2, 200, 20)) +\end_layout + +\end_inset + +the output must be: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +Seq(Seq(50, 30, 10), Seq(100, 10), Seq(200, 20, 2)) +\end_layout + +\end_inset + + \end_layout \begin_layout Standard @@ -8613,7 +8529,7 @@ x \end_inset - is an element from the set + is an element from \begin_inset listings inline true status open @@ -8637,7 +8553,7 @@ y \end_inset - is an element from the set + is an element from \begin_inset listings inline true status open @@ -8786,7 +8702,7 @@ noprefix "false" \end_inset -* + \end_layout \begin_layout Standard @@ -8865,7 +8781,7 @@ color{dkgreen} \end_inset o}$del paid 20. - On day 2, Church paid 100 and Gentzen paid 50, etc. + On day 2, Gentzen paid 50, etc. \end_layout \begin_layout Plain Layout @@ -8900,27 +8816,27 @@ o}$del \begin_inset Quotes eld \end_inset -Church +Gentzen \begin_inset Quotes erd \end_inset - -> 100, + -> 50), Map( \begin_inset Quotes eld \end_inset -Gentzen +Tarski \begin_inset Quotes erd \end_inset - -> 50), Map( + -> 50, \begin_inset Quotes eld \end_inset -Tarski +Church \begin_inset Quotes erd \end_inset - -> 50), Map( + -> 100), Map( \begin_inset Quotes eld \end_inset @@ -9029,8 +8945,7 @@ zip \end_inset . - These techniques are powerful but still insufficient for solving certain - problems. + These techniques are powerful but still insufficient for certain tasks. \end_layout \begin_layout Standard @@ -9213,7 +9128,7 @@ max \end_layout \begin_layout Standard -Another task not solvable with +Another task not easily solved with \begin_inset listings inline true status open @@ -9272,44 +9187,8 @@ res0: Double = 204.5 \end_inset -Why is it impossible to implement this function by using -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -map -\end_layout - -\end_inset - -, -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -sum -\end_layout - -\end_inset - -, -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -zip -\end_layout - -\end_inset - -, and other methods we have seen so far? In fact, the same task for integer - numbers (instead of floating-point numbers) +Note that the same task for integer numbers (instead of floating-point numbers) + \emph on can \emph default @@ -10041,7 +9920,7 @@ length \end_layout \begin_layout Standard -Base case: The length of an empty sequence is zero, so +Base case: The length of an empty sequence is zero, so we write: \begin_inset listings inline true status open @@ -10410,20 +10289,29 @@ status open \end_inset . - Then we get: + Then: \begin_inset listings inline false status open \begin_layout Plain Layout -((x +: xs) ++ ys).length == (x +: (xs ++ ys)).length == 1 + (xs.length + ys.length) +((x +: xs) ++ ys).length // Expect to equal (x +: xs).length + ys.length +\end_layout + +\begin_layout Plain Layout + + == (x +: (xs ++ ys)).length \end_layout \begin_layout Plain Layout - == (x +: xs).length + ys.length // Right-hand side of the property for - (x +: xs) + == 1 + (xs.length + ys.length) +\end_layout + +\begin_layout Plain Layout + + == (x +: xs).length + ys.length \end_layout \end_inset @@ -10608,22 +10496,7 @@ status open \end_inset , while the inductive step returns a value computed from the recursive call. - In this example, -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "53col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - + Look at this code: \begin_inset listings inline false status open @@ -10655,24 +10528,7 @@ def sum(s: Seq[Int]): Int = if (s.isEmpty) 0 else { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -the +The \begin_inset listings inline true status open @@ -10684,7 +10540,7 @@ if/else \end_inset - expression will separate the base case from the inductive step. + expression separates the base case from the inductive step. In the inductive step, it is convenient to split the given sequence \begin_inset listings inline true @@ -10944,21 +10800,6 @@ noprefix "false" \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "64col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -10990,23 +10831,6 @@ def digitsToInt(s: Seq[Int]): Int = if (s.isEmpty) 0 else { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In this example, it is important to split the sequence \begin_inset listings inline true @@ -11085,6 +10909,18 @@ digitsToInt(xs) by \begin_inset Formula $10$ +\end_inset + + before adding +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +x +\end_layout + \end_inset . @@ -11353,21 +11189,6 @@ status open . So, we can visualize how the computer evaluates that code: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "42.75col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -11394,23 +11215,6 @@ lengthS(Seq(1, 2, ..., 100000)) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The code of \begin_inset listings inline true @@ -11468,26 +11272,26 @@ if/else \end_inset thousand times. - Each time, the sub-expression with nested computations + Each time, the intermediate sub-expression with nested computations \begin_inset listings inline true status open \begin_layout Plain Layout -1+(1+(...)) +1 + (1 + (...)) \end_layout \end_inset will get larger. - This intermediate sub-expression needs to be held somewhere in memory until - the function body goes into the base case, with no more recursive calls. + That sub-expression needs to be held somewhere in memory until the function + body goes into the base case, with no more recursive calls. When that happens, the intermediate sub-expression will contain about -\begin_inset Formula $100$ +\begin_inset Formula $100000$ \end_inset - thousand nested function calls still waiting to be evaluated. + nested function calls still waiting to be evaluated. A special area of memory called \series bold @@ -11502,8 +11306,8 @@ stack memory stack memory \series default - is dedicated to storing the data for all not-yet-evaluated nested function - calls. + is dedicated to storing the arguments for all not-yet-evaluated nested + function calls. Due to the way computer memory is managed, the stack memory has a fixed size and cannot grow automatically. So, when the intermediate expression becomes large enough, it causes an @@ -11543,21 +11347,6 @@ itself the last computation in the function body, rather than placed inside other computations. Here is an example of tail-recursive code: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "42col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -110baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -11579,23 +11368,6 @@ def lengthT(s: Seq[Int], res: Int): Int = \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In this code, one of the branches of the \begin_inset listings inline true @@ -11805,6 +11577,10 @@ import scala.annotation.tailrec \begin_layout Plain Layout +\end_layout + +\begin_layout Plain Layout + @tailrec def lengthT(s: Seq[Int], res: Int): Int = \end_layout @@ -11821,21 +11597,6 @@ import scala.annotation.tailrec \end_inset Let us trace the evaluation of this function on an example: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "56.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -11868,18 +11629,6 @@ lengthT(Seq(1, 2, 3), 0) \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard @@ -12171,7 +11920,7 @@ status open \end_layout \begin_layout Standard -So, a tail-recursive implementation of +It appears useful to define the helper function ( \begin_inset listings inline true status open @@ -12183,11 +11932,20 @@ lengthT \end_inset - requires us to define -\emph on -two -\emph default - functions: the tail-recursive +) separately. + Then +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +length +\end_layout + +\end_inset + + will just call \begin_inset listings inline true status open @@ -12199,8 +11957,7 @@ lengthT \end_inset - and the main function that will set the initial value of the accumulator - argument. + and specify the initial value of the accumulator argument. To emphasize that \begin_inset listings inline true @@ -12213,17 +11970,58 @@ lengthT \end_inset - is a helper function, one could define it -\emph on -inside -\emph default - the main function: + is a helper function that is only used by +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +length +\end_layout + +\end_inset + + to achieve tail recursion, we define +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +lengthT +\end_layout + +\end_inset + + as a nested function inside the code of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +length +\end_layout + +\end_inset + +: \begin_inset listings inline false status open \begin_layout Plain Layout +import scala.annotation.tailrec +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\begin_layout Plain Layout + def length[A](xs: Seq[A]): Int = { \end_layout @@ -12377,7 +12175,8 @@ def f(x: Int, y: Boolean = false): Int = ... \end_inset -is equivalent to defining two functions (with the same name): +is equivalent to defining two functions with the same name but different + numbers of arguments: \begin_inset listings inline false status open @@ -12457,8 +12256,7 @@ Usually, the accumulator trick works because some associativity law is present. \end_layout \begin_layout Standard -As an example, let us implement a tail-recursive version of the function - +An example is a tail-recursive version of the function \begin_inset listings inline true status open @@ -12470,8 +12268,7 @@ digitsToInt \end_inset - from the previous subsection, where the recursive call is within a sub-expressi -on + from the previous subsection, where the sub-expression \begin_inset listings inline true status open @@ -12483,7 +12280,7 @@ digitsToInt(xs) * 10 + x \end_inset -. + was a non-tail-recursive call. To transform the code into a tail-recursive form, we need to rearrange the main computation: \begin_inset Formula @@ -12536,21 +12333,6 @@ s == s.head +: s.tail . So, a tail-recursive implementation of the above formula is: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "57col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -12591,23 +12373,6 @@ res0: Int = 1234 \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Despite a similarity between this code and the code of \begin_inset listings inline true @@ -13832,27 +13597,29 @@ sum(Seq(1, 2, 3)) == leftFold(Seq(1, 2, 3), 0, g) \begin_layout Plain Layout - // Here, g = (x, y) => x + y, so g(x, y) = x + y. + // Here, g = (x, y) => x + y, so g(x, y) = x + + y. \end_layout \begin_layout Plain Layout - == leftFold(Seq(2, 3), g(0, 1), g) // g (0, 1) = 1. + == leftFold(Seq(2, 3), g(0, 1), g) // g (0, 1) = + 1. \end_layout \begin_layout Plain Layout - == leftFold(Seq(2, 3), 1, g) // Now expand the code of `leftFold`. + == leftFold(Seq(2, 3), 1, g) // Now expand the code of `leftFold`. \end_layout \begin_layout Plain Layout - == leftFold(Seq(3), g(1, 2), g) // g(1, 2) = 3; expand the code. + == leftFold(Seq(3), g(1, 2), g) // g(1, 2) = 3; expand the code. \end_layout \begin_layout Plain Layout - == leftFold(Seq(), g(3, 3), g) // g(3, 3) = 6; expand the code. + == leftFold(Seq(), g(3, 3), g) // g(3, 3) = 6; expand the code. \end_layout \begin_layout Plain Layout @@ -14318,21 +14085,6 @@ count \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "55col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -14349,24 +14101,7 @@ def count[A](s: Seq[A], p: A => Boolean): Int = \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -The accumulator is of type +The accumulator has type \begin_inset listings inline true status open @@ -14517,7 +14252,7 @@ foldLeft \end_layout \begin_layout Subsection -Solved examples: using +Examples: Using \family typewriter foldLeft \family default @@ -14526,7 +14261,7 @@ foldLeft status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -14687,14 +14422,26 @@ xs :+ x \end_inset - is + is the maximum of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +b +\end_layout + +\end_inset + + and \begin_inset listings inline true status open \begin_layout Plain Layout -math.max(b, x) +x \end_layout \end_inset @@ -14710,8 +14457,8 @@ status open \begin_layout Plain Layout -def max(s: Seq[Int]): Int = s.foldLeft(Int.MinValue) { (b, x) => math.max(b, - x) } +def max(s: Seq[Int]): Int = s.foldLeft(Int.MinValue) { (b, x) => if (b > x) + b else x } \end_layout \end_inset @@ -14748,7 +14495,7 @@ status open \begin_layout Plain Layout -def max(s: Seq[Int]): Int = s.reduce { (x, y) => math.max(x, y) } +def max(s: Seq[Int]): Int = s.reduce { (x, y) => if (y > x) y else x } \end_layout \end_inset @@ -15011,7 +14758,7 @@ status open \begin_layout Plain Layout -(Double, Double, Double, Double) +(Double, Double, Double, Int) \end_layout \end_inset @@ -15467,7 +15214,7 @@ noprefix "false" \end_inset -* + \end_layout \begin_layout Standard @@ -15509,7 +15256,7 @@ Seq[Char] \end_inset . - As a test, the expression + As a test, \begin_inset listings inline true status open @@ -15556,7 +15303,7 @@ foldLeft \end_inset on a sequence of digits will visit the sequence from left to right. - The updating function should work the same as in + The updating function should work as in \begin_inset listings inline true status open @@ -15614,19 +15361,7 @@ Boolean \end_layout \begin_layout Standard -To see what -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -digitsToDouble -\end_layout - -\end_inset - - must do, let us consider how the evaluation of +Let us consider how the evaluation of \begin_inset listings inline true status open @@ -15732,7 +15467,7 @@ status open \begin_layout Plain Layout -\size small +\size footnotesize \begin_inset Formula $0.0$ \end_inset @@ -15746,7 +15481,7 @@ status open \begin_layout Plain Layout -\size small +\size footnotesize \begin_inset Formula $3.0$ \end_inset @@ -15782,7 +15517,7 @@ status open \begin_layout Plain Layout -\size small +\size footnotesize \begin_inset Formula $3.0$ \end_inset @@ -15796,7 +15531,7 @@ status open \begin_layout Plain Layout -\size small +\size footnotesize \begin_inset Formula $34.0$ \end_inset @@ -15832,7 +15567,7 @@ status open \begin_layout Plain Layout -\size small +\size footnotesize \begin_inset Formula $34.0$ \end_inset @@ -15846,7 +15581,7 @@ status open \begin_layout Plain Layout -\size small +\size footnotesize \begin_inset Formula $34.0$ \end_inset @@ -15882,7 +15617,7 @@ status open \begin_layout Plain Layout -\size small +\size footnotesize \begin_inset Formula $34.0$ \end_inset @@ -15896,7 +15631,7 @@ status open \begin_layout Plain Layout -\size small +\size footnotesize \begin_inset Formula $34.2$ \end_inset @@ -15932,7 +15667,7 @@ status open \begin_layout Plain Layout -\size small +\size footnotesize \begin_inset Formula $34.2$ \end_inset @@ -15946,7 +15681,7 @@ status open \begin_layout Plain Layout -\size small +\size footnotesize \begin_inset Formula $34.25$ \end_inset @@ -15985,8 +15720,8 @@ While the dot character was not yet seen, the updater function multiplies \begin_inset Formula \[ g(n,c)=\begin{cases} -n*10+c\quad, & \text{if the digit is before the dot character.}\\ -n+c/f\quad, & \text{if after the dot character, where }f=10,100,1000,...\text{ for each new digit.} +n*10+c & \text{if the digit is before the dot}\\ +n+c/f & \text{if after the dot, where }f=10,100,1000,...\text{ for each new digit} \end{cases} \] @@ -16110,8 +15845,8 @@ def update(acc: (Double, Boolean, Double), c: Char): (Double, Boolean, Double) \begin_layout Plain Layout - if (c == '.') (num, true, factor) // Set flag to `true` after a dot character - was seen. + if (c == '.') (num, true, factor) // Set flag to `true` after seeing + a dot. \end_layout \begin_layout Plain Layout @@ -16126,14 +15861,13 @@ def update(acc: (Double, Boolean, Double), c: Char): (Double, Boolean, Double) \begin_layout Plain Layout - if (flag) (num + digit / factor, flag, factor * 10) // This digit - is after the dot. + if (flag) (num + digit / factor, flag, factor * 10) // After the dot. \end_layout \begin_layout Plain Layout - else (num * 10 + digit, flag, factor) // This digit - is before the dot. + else (num * 10 + digit, flag, factor) // Before the + dot. \end_layout \begin_layout Plain Layout @@ -16161,21 +15895,6 @@ digitsToDouble \end_inset like this: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "48col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -16211,7 +15930,7 @@ def digitsToDouble(d: Seq[Char]): Double = { \begin_layout Plain Layout -scala> digitsToDouble(Seq('3','4','.','2','5')) +scala> digitsToDouble(Seq('3', '4', '.', '2', '5')) \end_layout \begin_layout Plain Layout @@ -16221,23 +15940,6 @@ res0: Double = 34.25 \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -120baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The result of calling \begin_inset listings inline true @@ -17008,22 +16710,7 @@ def toPairs[A](xs: Seq[A], default: A): Seq[(A, A)] = { \end_inset This code shows examples of partial functions that are applied safely. - One of these partial functions is used in the expression -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "30col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -95baselineskip% -\end_inset - - + One of these partial functions is used in this sub-expression: \begin_inset listings inline false status open @@ -17050,23 +16737,6 @@ holdover match { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent This code works when \begin_inset listings inline true @@ -17495,6 +17165,8 @@ filter \end_inset for sequences. + The predicate is applied to the elements of the initial sequence, and values + that pass the predicate are mapped. The required type signature and a sample test: \begin_inset listings inline false @@ -17543,7 +17215,7 @@ noprefix "false" \end_inset -* + \end_layout \begin_layout Standard @@ -17555,7 +17227,7 @@ batches \begin_inset Quotes erd \end_inset -) of length not larger than a given maximum length +) of length at most \begin_inset Formula $n$ \end_inset @@ -17567,7 +17239,7 @@ status open \begin_layout Plain Layout -def byLength[A](xs: Seq[A], length: Int): Seq[Seq[A]] = ??? +def byLength[A](xs: Seq[A], maxLength: Int): Seq[Seq[A]] = ??? \end_layout \begin_layout Plain Layout @@ -17621,7 +17293,7 @@ noprefix "false" \end_inset -* + \end_layout \begin_layout Standard @@ -17685,7 +17357,7 @@ noprefix "false" \end_inset -* + \end_layout \begin_layout Standard @@ -17757,14 +17429,14 @@ Map[K, Seq[A]] \end_inset . - To work with dictionaries, you will need to use the methods + Use the methods \begin_inset listings inline true status open \begin_layout Plain Layout -getOrElse +updated \end_layout \end_inset @@ -17776,12 +17448,12 @@ status open \begin_layout Plain Layout -updated +getOrElse \end_layout \end_inset -. + to work with dictionaries. The method \begin_inset listings inline true @@ -17794,8 +17466,8 @@ getOrElse \end_inset - fetches a value from a dictionary by key, and returns the given default - value if the dictionary does not contain that key: + fetches a value from a dictionary by key but returns a default value if + the key is not in the dictionary: \begin_inset listings inline false status open @@ -17952,21 +17624,6 @@ unfolding ) converts a single value into a sequence. An example of this task is to compute the sequence of decimal digits for a given integer: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "37col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -17992,23 +17649,6 @@ res0: Seq[Int] = List(2, 4, 0, 5) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent We cannot implement \begin_inset listings inline true @@ -18194,7 +17834,7 @@ Stream.iterate status open \begin_layout Plain Layout -In a future version of Scala 3, the +In Scala 3, the \begin_inset listings inline true status open @@ -18206,7 +17846,7 @@ Stream \end_inset - class will be replaced by + class is replaced by \begin_inset listings inline true status open @@ -18225,21 +17865,6 @@ LazyList This function has two arguments, the initial value and a function that computes the next value from the previous one: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -70baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -18256,23 +17881,6 @@ res0: Stream[Int] = Stream(2, ?) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The stream is ready to start computing the next elements of the sequence (so far, only the first element, \begin_inset listings @@ -18346,7 +17954,7 @@ takeWhile \end_layout \begin_layout Standard -Streams are similar to sequences, and methods such as +Streams have methods such as \begin_inset listings inline true status open @@ -18382,7 +17990,7 @@ flatMap \end_inset - are also defined for streams. + similar to sequences. For instance, the method \begin_inset listings inline true @@ -18549,23 +18157,11 @@ Here \begin_inset Formula $n=2405$ \end_inset -: +: \end_layout \begin_layout Standard -\begin_inset Wrap table -lines 0 -placement l -overhang 0in -width "47col%" -status open - -\begin_layout Plain Layout \align center -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset Tabular @@ -18924,22 +18520,9 @@ status open \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\noindent The numbers \begin_inset Formula $n_{k}$ \end_inset @@ -19036,7 +18619,7 @@ def digitsOf(n: Int): Seq[Int] = \end_inset -We can shorten the code by using the syntax such as +We can shorten the code by using the syntax \begin_inset listings inline true status open @@ -19162,6 +18745,13 @@ next \begin_layout Section Transforming a sequence into another sequence +\begin_inset CommandInset label +LatexCommand label +name "sec:Transforming-a-sequence" + +\end_inset + + \end_layout \begin_layout Standard @@ -19407,21 +18997,6 @@ foldLeft the second sequence together with a growing fragment of that sequence, which is updated as we iterate over the first sequence. The code is: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "68col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -95baselineskip% -\end_inset - - \begin_inset listings lstparams "numbers=left" inline false @@ -19474,23 +19049,6 @@ def scanLeft[A, B](xs: Seq[A])(b0: B)(next: (B, A) => B): Seq[B] = { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -120baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent To implement the (nameless) updater function for \begin_inset listings inline true @@ -19642,19 +19200,13 @@ transform existing sequences into new sequences. \end_deeper \begin_layout Standard -\begin_inset Wrap table -lines 0 -placement l -overhang 0in -width "59col%" +\begin_inset Float table +wide false +sideways false status open \begin_layout Plain Layout \align center -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset Tabular @@ -19857,12 +19409,8 @@ name "tab:Implementing-mathematical-induction" \end_inset -\end_layout - -\end_inset - - -\begin_inset VSpace -75baselineskip% +\end_layout + \end_inset @@ -19874,7 +19422,6 @@ name "tab:Implementing-mathematical-induction" \end_layout \begin_layout Standard -\noindent Table \begin_inset space ~ \end_inset @@ -19980,18 +19527,18 @@ trampolines \end_inset -) that convert non-tail-recursive code into iterative code without stack +) that convert non-tail-recursive code into code that runs without stack overflows. - Those techniques are beyond the scope of this book. + Those techniques are beyond the scope of this chapter. \end_layout \begin_layout Subsection -Solved examples +Examples \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -21149,7 +20696,7 @@ res0: Int = 2 \series bold (b) \series default - Re-implement + Implement \begin_inset listings inline true status open @@ -21173,7 +20720,7 @@ Stream.iterate \end_inset - without writing explicitly recursive code. + without explicit recursion. \end_layout \begin_layout Subparagraph @@ -21313,7 +20860,7 @@ right \end_inset -) representing +) for \begin_inset Formula $i$ \end_inset @@ -21680,14 +21227,12 @@ def binSearch(xs: Seq[Int], goal: Int): Int = { \begin_layout Plain Layout - .find { case (x, y) => y - x <= 1 } // Find the element with tight enough - bounds. + .find { case (x, y) => y - x <= 1 } // Find an element with tight bounds. \end_layout \begin_layout Plain Layout - .get._1 // Take the `left` bound from that - element. + .get._1 // Take the `left` bound from that. \end_layout \begin_layout Plain Layout @@ -22020,29 +21565,29 @@ status open \begin_layout Plain Layout -def sdSeq(n: Int): Seq[Int] = Stream.iterate(n)(SD) // Stream[Int] +def sdSeq(n: Int): Seq[Int] = Stream.iterate(n)(SD) // Stream[Int] \end_layout \begin_layout Plain Layout - .scanLeft((0,0)) { case ((prev, x), next) => (x, next) } // Stream[(Int, - Int)] + .scanLeft((0,0)) { case ((prev, x), next) => (x, next) } // Transform + to Stream[(Int, Int)]. \end_layout \begin_layout Plain Layout - .drop(1).takeWhile { case (x, y) => x != y } // Stream[(Int, - Int)] + .drop(1).takeWhile { case (x, y) => x != y } // Still Stream[(Int, + Int)]. \end_layout \begin_layout Plain Layout - .map(_._2) // Stream[Int] + .map(_._2) // Stream[Int] \end_layout \begin_layout Plain Layout - .toList // List[Int] + .toList // List[Int] \end_layout \begin_layout Plain Layout @@ -22226,7 +21771,7 @@ next(init) == None \end_inset -, the stream must stop at +, the stream will have just one value ( \begin_inset listings inline true status open @@ -22238,8 +21783,8 @@ init \end_inset -. - (This is the base case of the induction). +). + This is the base case of the induction. Otherwise, \begin_inset listings inline true @@ -22264,7 +21809,8 @@ x \end_inset - and indicates that we need to continue to +. + So, we need to continue to \begin_inset Quotes eld \end_inset @@ -22297,8 +21843,8 @@ init \end_inset . - (This is the inductive step.) Streams can be created from individual values - via the Scala standard library method + (This is the inductive step.) To create streams with given values, we use + the Scala library method \begin_inset listings inline true status open @@ -22310,7 +21856,8 @@ Stream.cons \end_inset - that constructs a stream from a single value and a tail: +. + It constructs a stream from a head value and a tail stream: \begin_inset listings inline false status open @@ -22323,14 +21870,13 @@ def unfold[A](init: A)(next: A => Option[A]): Stream[A] = next(init) match \begin_layout Plain Layout - case None => Stream(init) // A stream containing - a single value `init`. + case None => Stream(init) // A stream having a single value `init`. \end_layout \begin_layout Plain Layout - case Some(x) => Stream.cons(init, unfold(x)(next)) // `init` followed - by the tail of stream. + case Some(x) => Stream.cons(init, unfold(x)(next)) // `init` and then + the tail of the stream. \end_layout \begin_layout Plain Layout @@ -23568,8 +23114,7 @@ def longestNoDups[A](xs: Seq[A]): Seq[A] = { \begin_layout Plain Layout - // If `current` is empty, `x` is not considered to be - repeated. + // If `current` is empty, `x` is not considered to be repeated. \end_layout \begin_layout Plain Layout @@ -23584,13 +23129,12 @@ def longestNoDups[A](xs: Seq[A]): Seq[A] = { \begin_layout Plain Layout - // Compute the new pair `(first, current)`. + // Compute the new pair `(first, current)`. \end_layout \begin_layout Plain Layout - // Keep `first` only if it is longer; otherwise replace - it by `current`. + // Keep `first` only if it is longer; otherwise replace it by `current`. \end_layout \begin_layout Plain Layout @@ -23600,7 +23144,7 @@ def longestNoDups[A](xs: Seq[A]): Seq[A] = { \begin_layout Plain Layout - // Append `x` to `current` if `x` is not repeated. + // Append `x` to `current` if `x` is not repeated. \end_layout \begin_layout Plain Layout @@ -23620,8 +23164,7 @@ def longestNoDups[A](xs: Seq[A]): Seq[A] = { \begin_layout Plain Layout - // Return the longer of the two subsequences; prefer - `first`. + // Return the longer of the two subsequences; prefer `first`. \end_layout \begin_layout Plain Layout @@ -23675,7 +23218,20 @@ noprefix "false" \end_layout \begin_layout Standard -Compute the sum of squared digits of a given integer; e.g., +Define a function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +dsq +\end_layout + +\end_inset + + that computes the sum of squared digits of a given integer; for example, + \begin_inset listings inline true status open @@ -23702,7 +23258,19 @@ noprefix "false" \end_inset ). - Generalize the solution to take as an argument an function + Generalize +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +dsq +\end_layout + +\end_inset + + to take as an argument a function \begin_inset listings inline true status open @@ -24194,7 +23762,7 @@ noprefix "false" \end_inset -* + \end_layout \begin_layout Standard @@ -24336,7 +23904,7 @@ noprefix "false" \end_inset -* + \end_layout \begin_layout Standard @@ -24797,7 +24365,7 @@ xs: Seq[Int] \end_inset - into a sequence + into a sequence of type \begin_inset listings inline true status open @@ -24863,7 +24431,7 @@ def skip1[A](xs: Seq[A]): Seq[(A, A)] = ??? \begin_layout Plain Layout -scala> skip1(List(1,2,3,4,5)) +scala> skip1(List(1, 2, 3, 4, 5)) \end_layout \begin_layout Plain Layout @@ -25050,19 +24618,8 @@ noprefix "false" \end_layout \begin_layout Standard -Remove adjacent repeated elements from a sequence of type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Seq[A] -\end_layout - -\end_inset - - when they are repeated more than +Transform a sequence by removing adjacent repeated elements when they are + repeated more than \begin_inset Formula $k$ \end_inset @@ -25287,7 +24844,7 @@ noprefix "false" \end_inset -* + \end_layout \begin_layout Standard @@ -25309,7 +24866,7 @@ Seq[A] \end_inset . - (This re-implements the standard library's method + (This reproduces the standard library's method \begin_inset listings inline true status open @@ -25457,7 +25014,7 @@ noprefix "false" \end_inset -* + \end_layout \begin_layout Standard @@ -25560,7 +25117,7 @@ noprefix "false" \end_inset -* + \end_layout \begin_layout Standard @@ -26430,13 +25987,13 @@ infinite \end_layout \begin_layout Standard -Generally, there are four possible ways a value could be available: +Generally, there are three possible ways a value could be available: \end_layout \begin_layout Standard \align center \begin_inset Tabular - + @@ -26567,7 +26124,7 @@ lazy \begin_layout Plain Layout \size small -computed upon first use +computed upon first use and stored \end_layout \end_inset @@ -26590,59 +26147,6 @@ lazy val z = f(123) \end_inset -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Quotes eld -\end_inset - -on-call -\begin_inset Quotes erd -\end_inset - - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -computed each time it is used -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -def z = f(123) -\end_layout - -\end_inset - - \end_layout \end_inset @@ -26658,7 +26162,7 @@ def z = f(123) \begin_inset Quotes eld \end_inset -never +on-call \begin_inset Quotes erd \end_inset @@ -26673,7 +26177,7 @@ never \begin_layout Plain Layout \size small -cannot be computed due to errors +computed each time it is needed \end_layout \end_inset @@ -26690,15 +26194,7 @@ status open \begin_layout Plain Layout -val (x, y) = -\begin_inset Quotes eld -\end_inset - -abc -\begin_inset Quotes erd -\end_inset - - +def z = f(123) \end_layout \end_inset @@ -26779,7 +26275,7 @@ def \end_inset - declaration does that. + declaration does that (as well as call-by-name arguments). \end_layout \begin_layout Standard @@ -26931,8 +26427,7 @@ status open \begin_layout Plain Layout -scala> (1L to 1000000000L).sum // Compute the sum of integers from - 1 to 1 billion. +scala> (1L to 1000000000L).sum // Compute the sum from 1 to 1 billion. \end_layout \begin_layout Plain Layout @@ -26942,13 +26437,13 @@ res0: Long = 500000000500000000 \end_inset -We do not actually need to put a billion numbers in memory if we only want - to compute their sum. +We do not actually need to store a billion numbers in memory if we only + want to compute their sum. Indeed, the computation just shown does \emph on not \emph default - put all the numbers in memory. + store all the numbers in memory. The computation will fail if we use a list or a stream: \begin_inset listings inline false @@ -27211,21 +26706,6 @@ flatMap \end_inset would require too much memory, and the computation would crash: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "54.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -27242,20 +26722,6 @@ java.lang.OutOfMemoryError: GC overhead limit exceeded \end_inset - -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Even though the range \begin_inset listings inline true @@ -27295,21 +26761,6 @@ view \end_inset to avoid this: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "54.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -27327,19 +26778,9 @@ res0: Int = 3000000 \end_inset -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\noindent The choice between using streams and using iterators is dictated by memory constraints. Except for that, streams and iterators behave similarly to other sequences. @@ -27401,7 +26842,7 @@ status open \begin_layout Plain Layout -Iterator.iterate +iterate \end_layout \end_inset @@ -27856,7 +27297,7 @@ res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9) \end_inset -Iterators produced by applying the +The \begin_inset listings inline true status open @@ -27868,7 +27309,11 @@ view \end_inset - method to collections will have value-like behavior: + method produces iterators that +\emph on +do +\emph default + have value-like behavior: \begin_inset listings inline false status open @@ -27929,7 +27374,7 @@ Iterator \end_inset - may not obey the usual rules of mathematical reasoning. + do not obey the usual rules of mathematical reasoning. \begin_inset Index idx status open @@ -28105,7 +27550,8 @@ iter \end_inset - is used twice in this code, which leads to errors because + is used twice in this code, which leads to errors. + Apparently, \begin_inset listings inline true status open @@ -28117,8 +27563,11 @@ iter \end_inset - is mutable and does not behave as a value. - Creating an + +\emph on +does not behave as a value +\emph default +! Creating an \begin_inset listings inline true status open @@ -28162,21 +27611,7 @@ java.util.NoSuchElementException: next on empty iterator \end_inset -It is surprising and counter-intuitive that a variable should not be used - twice in an expression. - Intuitively, we expect the code -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -s.zip(s) -\end_layout - -\end_inset - - to work correctly even though the variable +It is surprising and counter-intuitive that a variable (here, \begin_inset listings inline true status open @@ -28188,8 +27623,7 @@ s \end_inset - is used twice. - When we read the expression +) cannot be used twice; we expect that the code \begin_inset listings inline true status open @@ -28201,40 +27635,27 @@ s.zip(s) \end_inset -, we imagine a given sequence -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -s -\end_layout - -\end_inset - - being + would just \begin_inset Quotes eld \end_inset -zipped +zip \begin_inset Quotes erd \end_inset - with itself. - So, we reason that + a given sequence \begin_inset listings inline true status open \begin_layout Plain Layout -s.zip(s) +s \end_layout \end_inset - should produce a sequence of pairs. + with itself. But Scala's \begin_inset listings inline true @@ -28357,7 +27778,7 @@ scala> str.toList \begin_layout Plain Layout -res0: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9) +res0: List[Int] = List(1, 2, 3, 4, 5, 6) \end_layout \begin_layout Plain Layout @@ -28371,7 +27792,7 @@ scala> str.toList \begin_layout Plain Layout -res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9) +res1: List[Int] = List(1, 2, 3, 4, 5, 6) \end_layout \begin_layout Plain Layout @@ -28385,8 +27806,8 @@ scala> str.zip(str).toList \begin_layout Plain Layout -res2: List[(Int, Int)] = List((1,1), (2,2), (3,3), (4,4), (5,5), (6,6), - (7,7), (8,8), (9,9)) +res2: List[(Int, Int)] = List((1,1), (2,2), (3,3), (4,4), (5,5), (6,6)) + \end_layout \end_inset @@ -28456,7 +27877,7 @@ LazyList \end_inset -, which is a lazy (and possibly infinite) stream. +, which is a lazily evaluated (and possibly infinite) stream. Libraries such as \begin_inset listings inline true @@ -28481,11 +27902,11 @@ fs2 \end_inset - also provide lazy and on-call streams with correct value-like behavior. + also provide streams with correct value-like behavior. \end_layout \begin_layout Standard -The self-modifying behavior of +The mutable behavior of \begin_inset listings inline true status open @@ -28556,5 +27977,81 @@ Pure functions So, pure functions behave similarly to functions that are used in mathematics. \end_layout +\begin_layout Standard +This book focuses on pure functions and on mathematical reasoning about + them. + Statements such as +\emph on + +\begin_inset Quotes eld +\end_inset + +the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +map +\end_layout + +\end_inset + + method cannot implement +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +sum +\end_layout + +\end_inset + + because it can only apply element-wise transformations to sequences +\begin_inset Quotes erd +\end_inset + + +\emph default + are correct only if the code is restricted to pure functions without side + effects. + Otherwise we would write code like this: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def sum(xs: Seq[Int]): Int = { +\end_layout + +\begin_layout Plain Layout + + var result: Int = 0 // A mutable variable! +\end_layout + +\begin_layout Plain Layout + + sum.map { x => result += x } // Side effect: mutation. +\end_layout + +\begin_layout Plain Layout + + result +\end_layout + +\begin_layout Plain Layout + +} +\end_layout + +\end_inset + + +\end_layout + \end_body \end_document diff --git a/sofp-src/sofp-induction.tex b/sofp-src/sofp-induction.tex index df4afdbd2..a3f2f5a63 100644 --- a/sofp-src/sofp-induction.tex +++ b/sofp-src/sofp-induction.tex @@ -29,13 +29,12 @@ \subsection{Examples of using tuples} val b: (Boolean, Int, Int) = (true, 3, 4) \end{lstlisting} Pairs and triples are examples of tuples. A \textbf{tuple} can contain -any number of values, which may be called \textbf{parts\index{tuples!parts}} -of a tuple (they are also called \textbf{fields} of\index{tuples!fields} -a tuple). The parts of a tuple can have different types, but the type -of each part is fixed once and for all. Also, the number of parts -in a tuple is fixed. It is a \textbf{type error}\index{type error} -to use incorrect types in a tuple, or an incorrect number of parts -of a tuple: +several values called \textbf{parts\index{tuples!parts}} or \textbf{fields} +of\index{tuples!fields} a tuple. A tuple\textsf{'}s parts can have different +types, but the type of each part (and the number of parts) is fixed +once and for all. It is a \textbf{type error}\index{type error} to +use incorrect types in a tuple, or an incorrect number of parts of +a tuple: \begin{lstlisting} scala> val bad: (Int, String) = (1,2) :11: error: type mismatch; @@ -116,8 +115,8 @@ \subsection{Pattern matching for tuples} \item destructuring definition: \lstinline[mathescape=true]!val $pattern$ = ...! \item \lstinline!case! expression: \lstinline[mathescape=true]!case $pattern$ => ...! \end{itemize} -\begin{wrapfigure}{l}{0.295\columnwidth}% -\vspace{-0.8\baselineskip} +Here is an example of a \textbf{destructuring\index{destructuring} +definition}: \begin{lstlisting} scala> val g = (1, 2, 3) g: (Int, Int, Int) = (1,2,3) @@ -127,18 +126,14 @@ \subsection{Pattern matching for tuples} y: Int = 2 z: Int = 3 \end{lstlisting} -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -An example of a \textbf{destructuring\index{destructuring} definition} -is shown at left. The value \lstinline!g! is a tuple of three integers. -After defining \lstinline!g!, we define the three variables \lstinline!x!, -\lstinline!y!, \lstinline!z! \emph{at once} in a single \lstinline!val! -definition. We imagine that this definition \textsf{``}destructures\textsf{''} the -data structure contained in \lstinline!g! and decomposes it into -three parts, then assigns the names \lstinline!x!, \lstinline!y!, -\lstinline!z! to these parts. The types of \lstinline!x!, \lstinline!y!, -\lstinline!z! are also assigned automatically. +The value \lstinline!g! is a tuple of three integers. After defining +\lstinline!g!, we define the three variables \lstinline!x!, \lstinline!y!, +\lstinline!z! \emph{at once} in a single \lstinline!val! definition. +We imagine that this definition \textsf{``}destructures\textsf{''} the data structure +contained in \lstinline!g! and decomposes it into three parts, then +assigns the names \lstinline!x!, \lstinline!y!, \lstinline!z! to +these parts. The types of \lstinline!x!, \lstinline!y!, \lstinline!z! +are also assigned automatically. In the example above, the left-hand side of the destructuring definition contains a tuple pattern \lstinline!(x, y, z)! that looks like a @@ -150,32 +145,28 @@ \subsection{Pattern matching for tuples} a tuple with exactly three parts, the definition will fail.) This computation is called \textbf{pattern matching}\index{pattern matching}. -Pattern matching is often used for working with tuples. The expression -\lstinline!{case (a, b, c) => ...}!\begin{wrapfigure}{l}{0.6\columnwidth}% -\vspace{-0.7\baselineskip} +Pattern matching is often used for working with tuples. Look at this +example: \begin{lstlisting} scala> (1, 2, 3) match { case (a, b, c) => a + b + c } res0: Int = 6 \end{lstlisting} -\vspace{-1.5\baselineskip} -\end{wrapfigure}% -called a \textbf{case expression}\index{case expression@\texttt{case} expression} -(shown at left) performs pattern matching on its argument. The pattern -matching will \textsf{``}destructure\textsf{''} (i.e., decompose) a tuple and try -to match it to the given pattern \lstinline!(a, b, c)!. In this pattern, -\lstinline!a!, \lstinline!b!, \lstinline!c! are as yet undefined -new variables, \textemdash{} that is, they are \index{pattern variables}pattern -variables. If the pattern matching succeeds, the pattern variables -\lstinline!a!, \lstinline!b!, \lstinline!c! are assigned their -values, and the function body can proceed to perform its computation. -In this example, the pattern variables \lstinline!a!, \lstinline!b!, -\lstinline!c! will be assigned values $1$, $2$, and $3$, and so -the expression evaluates to $6$. +The expression \lstinline!{case (a, b, c) => ...}! called a \textbf{case +expression}\index{case expression@\texttt{case} expression}. It performs +pattern matching on its argument. The pattern matching will \textsf{``}destructure\textsf{''} +(i.e., decompose) a tuple and try to match it to the given pattern +\lstinline!(a, b, c)!. In this pattern, \lstinline!a!, \lstinline!b!, +\lstinline!c! are as yet undefined new variables, \textemdash{} that +is, they are \index{pattern variables}pattern variables. If the pattern +matching succeeds, the pattern variables \lstinline!a!, \lstinline!b!, +\lstinline!c! are assigned their values, and the function body can +proceed to perform its computation. In this example, the pattern variables +\lstinline!a!, \lstinline!b!, \lstinline!c! will be assigned values +$1$, $2$, and $3$, and so the expression evaluates to $6$. Pattern matching is especially convenient for nested tuples. Here is an example where a nested tuple \lstinline!p! is destructured -by pattern matching:\hfill{}~\begin{wrapfigure}{L}{0.52\columnwidth}% -\vspace{-0.8\baselineskip} +by pattern matching: \begin{lstlisting} def t1(p: (Int, (String, Int))): String = p match { case (x, (str, y)) => str + (x + y).toString @@ -184,11 +175,7 @@ \subsection{Pattern matching for tuples} scala> t1((10, ("result is ", 2))) res0: String = result is 12 \end{lstlisting} -\vspace{-1.5\baselineskip} -\end{wrapfigure}% -\textbf{ } - -\noindent The type structure of the argument \lstinline!(Int, (String, Int))! +The type structure of the argument \lstinline!(Int, (String, Int))! is visually repeated in the pattern \lstinline!(x, (str, y))!, making it clear that \lstinline!x! and \lstinline!y! become integers and \lstinline!str! becomes a string after pattern matching. @@ -210,36 +197,26 @@ \subsection{Pattern matching for tuples} the function \lstinline!t3! computes the same expression as \lstinline!t2!. In contrast, the code is changed easily when using the pattern matching expression instead of the accessor methods. We only need to change -the type and the pattern:\hfill{}~\begin{wrapfigure}{L}{0.52\columnwidth}% -\vspace{-0.8\baselineskip} +the type and the pattern: \begin{lstlisting} def t4(p: ((Int, String), Int)): String = p match { case ((x, str), y) => str + (x + y).toString } \end{lstlisting} -\vspace{-0.6\baselineskip} -\end{wrapfigure}% - -\noindent It is easy to see that \lstinline!t4! and \lstinline!t1! -compute the same result. Also, the names of pattern variables may -be chosen to get more clarity. +It is easy to see that \lstinline!t4! and \lstinline!t1! compute +the same result. Also, the names of pattern variables may be chosen +to get more clarity. Sometimes we only need to use certain parts of a tuple in a pattern match. The following syntax is used to make that clear: - -\begin{wrapfigure}{L}{0.52\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} scala> val (x, _, _, z) = ("abc", 123, false, true) x: String = abc z: Boolean = true \end{lstlisting} -\vspace{-0.6\baselineskip} -\end{wrapfigure}% - -\noindent The underscore symbol (\lstinline!_!) denotes the parts -of the pattern that we want to ignore. The underscore will always -match any value regardless of its type. +The underscore symbol (\lstinline!_!) denotes the parts of the pattern +that we want to ignore. The underscore will always match any value +regardless of its type. Scala has a shorter syntax for functions such as \lstinline!{case (x, y) => y}! that extract elements from tuples. The syntax looks like \lstinline!(t => t._2)! @@ -268,6 +245,9 @@ \subsection{Using tuples with collections} For instance, we can define a tuple of functions: \begin{lstlisting} val q: (Int => Int, Int => Int) = (x => x + 1, x => x - 1) + +scala> q._1(3) +res0: Int = 4 \end{lstlisting} We can create a list of tuples: \begin{lstlisting} @@ -286,10 +266,10 @@ \subsection{Using tuples with collections} basket: List[(String, Int)] = List((apples,3), (pears,2), (lemons,0)) scala> basket.map { case (fruit, count) => count * 2 } -res0: List[Int] = List(6, 4, 0) +res1: List[Int] = List(6, 4, 0) scala> basket.map { case (fruit, count) => count * 2 }.sum -res1: Int = 10 +res2: Int = 10 \end{lstlisting} In this way, we can use the standard methods such as \lstinline!map!, \lstinline!filter!, \lstinline!max!, \lstinline!sum! to manipulate @@ -304,7 +284,7 @@ \subsection{Using tuples with collections} val isAcidic = (fruit == "lemons") (fruit, isAcidic) } -res2: List[(String, Boolean)] = List((apples,false), (pears,false), (lemons,true)) +res3: List[(String, Boolean)] = List((apples,false), (pears,false), (lemons,true)) \end{lstlisting} In the Scala syntax, a nameless function written with braces \lstinline!{ ... }! may define local values in its body. The return value of the function @@ -333,7 +313,7 @@ \subsection{Treating dictionaries as collections} List(("apples", 3), ("oranges", 2), ("pears", 0)).toMap \end{lstlisting} The same method works for other collection types such as \lstinline!Seq!, -\lstinline!Vector!, \lstinline!Stream!, and \lstinline!Array!. +\lstinline!Vector!, and \lstinline!Array!. The Scala library defines a special infix syntax for pairs via the arrow symbol \lstinline!->!. The expression \lstinline!x -> y! is @@ -448,14 +428,16 @@ \subsection{Treating dictionaries as collections} \paragraph*{The method \texttt{flatten}} -converts nested sequences to \textsf{``}flattened\textsf{''} ones: +converts a nested sequence type, such as \lstinline!List[List[A]]!, +into a simple \lstinline!List[A]! by concatenating all inner sequences +into one: \begin{lstlisting} scala> List(List(1, 2), List(2, 3), List(3, 4)).flatten res6: List[Int] = List(1, 2, 2, 3, 3, 4) \end{lstlisting} -Here, \textsf{``}flattening\textsf{''} means the concatenation of the inner sequences. -In Scala, sequences are concatenated using the operation \lstinline!++!. -For example: +In Scala, sequences and other collections (such as sets and dictionaries) +are generally concatenated using the operation \lstinline!++!. For +example: \begin{lstlisting} scala> List(1, 2, 3) ++ List(4, 5, 6) ++ List(0) res7: List[Int] = List(1, 2, 3, 4, 5, 6, 0) @@ -478,13 +460,13 @@ \subsection{Treating dictionaries as collections} is closely related to \lstinline!flatten! and can be seen as a shortcut, equivalent to first applying \lstinline!map! and then \lstinline!flatten!: \begin{lstlisting} -scala> List(1,2,3,4).map(n => (1 to n).toList) +scala> List(1, 2, 3, 4).map(n => (1 to n).toList) res9: List[List[Int]] = List(List(1), List(1, 2), List(1, 2, 3), List(1, 2, 3, 4)) -scala> List(1,2,3,4).map(n => (1 to n).toList).flatten +scala> List(1, 2, 3, 4).map(n => (1 to n).toList).flatten res10: List[Int] = List(1, 1, 2, 1, 2, 3, 1, 2, 3, 4) -scala> List(1,2,3,4).flatMap(n => (1 to n).toList) +scala> List(1, 2, 3, 4).flatMap(n => (1 to n).toList) res11: List[Int] = List(1, 1, 2, 1, 2, 3, 1, 2, 3, 4) \end{lstlisting} The \lstinline!flatMap! operation transforms a sequence by mapping @@ -507,8 +489,8 @@ \subsection{Treating dictionaries as collections} the letter \lstinline!"y"! into one subsequence, and all other words into another subsequence. This is accomplished by the following code: \begin{lstlisting} -scala> Seq("wombat", "xenon", "yogurt", "zebra").groupBy(s => if (s startsWith "y") 1 else 2) -res12: Map[Int,Seq[String]] = Map(1 -> List(yogurt), 2 -> List(wombat, xenon, zebra)) +scala> Seq("xenon", "yogurt", "zebra").groupBy(s => if (s startsWith "y") 1 else 2) +res12: Map[Int,Seq[String]] = Map(1 -> List(yogurt), 2 -> List(xenon, zebra)) \end{lstlisting} The argument of the \lstinline!groupBy! method is a \emph{function} that computes a \textsf{``}key\textsf{''} out of each sequence element. The key can @@ -571,13 +553,14 @@ \subsection{Treating dictionaries as collections} \end{lstlisting} -\subsection{Solved examples: Tuples and collections\index{solved examples}} +\subsection{Examples: Tuples and collections\index{examples (with code)}} \subsubsection{Example \label{enu:tuples-Example1-for}\ref{enu:tuples-Example1-for}} -For a given sequence $x_{i}$, compute the sequence of pairs $b_{i}=\left(\cos x_{i},\sin x_{i}\right)$. +For a given sequence $x_{i}$, compute the sequence of pairs of values +$\left(\cos x_{i},\sin x_{i}\right)$. -Hint: use \lstinline!map!, assume \lstinline!xs:Seq[Double]!. +Hint: use \lstinline!map!, assume \lstinline!xs: Seq[Double]!. \subparagraph{Solution} @@ -594,7 +577,7 @@ \subsubsection{Example \label{enu:tuples-Example2}\ref{enu:tuples-Example2}} Count how many times $\cos x_{i}>\sin x_{i}$ occurs in a sequence $x_{i}$. -Hint: use \lstinline!count!, assume \lstinline!xs:Seq[Double]!. +Hint: use \lstinline!count!, assume \lstinline!xs: Seq[Double]!. \subparagraph{Solution } @@ -615,12 +598,11 @@ \subsubsection{Example \label{enu:tuples-Example2}\ref{enu:tuples-Example2}} \subsubsection{Example \label{enu:tuples-Example3}\ref{enu:tuples-Example3}} -For given sequences $a_{i}$ and $b_{i}$, compute the sequence of -differences $c_{i}=a_{i}-b_{i}$. +For given sequences $a_{i}$ and $b_{i}$ of \lstinline!Double! values, +compute the sequence of differences $c_{i}=a_{i}-b_{i}$. Hint: use \lstinline!zip!, \lstinline!map!, and assume \lstinline!as! -and \lstinline!bs! are of type \lstinline!Seq[Double]! and have -equal length. +and \lstinline!bs! have equal length. \subparagraph{Solution } @@ -645,7 +627,7 @@ \subsubsection{Example \label{enu:tuples-Example4-in}\ref{enu:tuples-Example4-in \subparagraph{Solution} -Given \lstinline!ps:Seq[Double]!, we can compute \lstinline!ps.tail!. +Given \lstinline!ps: Seq[Double]!, we can compute \lstinline!ps.tail!. The result is a sequence that is $1$ element shorter than \lstinline!ps!, for example: \begin{lstlisting} @@ -657,21 +639,15 @@ \subsubsection{Example \label{enu:tuples-Example4-in}\ref{enu:tuples-Example4-in \end{lstlisting} Taking a \lstinline!zip! of the two sequences \lstinline!ps! and \lstinline!ps.tail!, we get a sequence of pairs: - -\begin{wrapfigure}{l}{0.53\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} scala> ps.zip(ps.tail) res1: Seq[(Int, Int)] = List((1,2), (2,3), (3,4)) \end{lstlisting} -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent Note that \lstinline!ps.tail! is one element shorter than -\lstinline!ps!, and the resulting sequence of pairs is also one element -shorter than \lstinline!ps!. So, it is not necessary to truncate -\lstinline!ps! before computing \lstinline!ps.zip(ps.tail)!. Now -apply the \lstinline!count! method: +Note that \lstinline!ps.tail! is one element shorter than \lstinline!ps!, +and the resulting sequence of pairs is also one element shorter than +\lstinline!ps!. So, it is not necessary to truncate \lstinline!ps! +before computing \lstinline!ps.zip(ps.tail)!. Now apply the \lstinline!count! +method: \begin{lstlisting} ps.zip(ps.tail).count { case (a, b) => a > b } @@ -699,17 +675,18 @@ \subsubsection{Example \label{subsec:tuples-Example5}\ref{subsec:tuples-Example5 of each of the nested lists by using the \lstinline!map! method on the outer list, with the \lstinline!max! method applied to the nested lists. So, the argument of the \lstinline!map! method must be the -function \lstinline!nested => nested.max!: +function \lstinline!x => x.max! (where \lstinline!x! will have type +\lstinline!List[Int!): \begin{lstlisting} -def c(b: List[Int], k: Int) = b.sliding(2 * k + 1).toList.map(nested => nested.max) +def c(b: List[Int], k: Int) = b.sliding(2 * k + 1).toList.map(x => x.max) \end{lstlisting} -In Scala, this code can be written more concisely using the syntax: +This code can be written more concisely using the syntax: \begin{lstlisting} def c(b: List[Int], k: Int) = b.sliding(2 * k + 1).toList.map(_.max) \end{lstlisting} -because \lstinline!_.max! means the nameless function \lstinline!x => x.max!. -Let us test this code: +because, in Scala, \lstinline!_.max! is the same as the nameless +function \lstinline!x => x.max!. Test this: \begin{lstlisting} scala> c(b = List(1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1), k = 1) // Write the argument names for clarity. res0: Seq[Int] = List(3, 4, 5, 6, 6, 6, 5, 4, 3) @@ -718,9 +695,9 @@ \subsubsection{Example \label{subsec:tuples-Example5}\ref{subsec:tuples-Example5 \subsubsection{Example \label{subsec:tuples-Example6}\ref{subsec:tuples-Example6}} -Create a $10\times10$ multiplication table as a dictionary of type -\lstinline!Map[(Int, Int), Int]!. For example, a $3\times3$ multiplication -table would be given by this dictionary: +Create a $10\times10$ multiplication table as a dictionary having +the type \lstinline!Map[(Int, Int), Int]!. For example, a $3\times3$ +multiplication table would be given by this dictionary: \begin{lstlisting} Map( (1, 1) -> 1, (1, 2) -> 2, (1, 3) -> 3, (2, 1) -> 2, (2, 2) -> 4, (2, 3) -> 6, (3, 1) -> 3, (3, 2) -> 6, (3, 3) -> 9 ) @@ -744,18 +721,12 @@ \subsubsection{Example \label{subsec:tuples-Example6}\ref{subsec:tuples-Example6 We would like to get \lstinline!List((1,1), (1,2), 1,3))! etc., and so we use \lstinline!map! on the inner list with a nameless function \lstinline!y => (1, y)! that converts a number into a tuple: - -\begin{wrapfigure}{l}{0.53\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} scala> List(1, 2, 3).map { y => (1, y) } res0: List[(Int, Int)] = List((1,1), (1,2), (1,3)) \end{lstlisting} -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent The curly braces in \lstinline!{y => (1, y)}! are only -for clarity. We could also use round parentheses and write \lstinline!List(1, 2, 3).map(y => (1, y))!. +The curly braces in \lstinline!{y => (1, y)}! are only for clarity. +We could also use round parentheses and write \lstinline!List(1, 2, 3).map(y => (1, y))!. Now, we need to have \lstinline!(x, y)! instead of \lstinline!(1, y)! in the argument of \lstinline!map!, where \lstinline!x! iterates @@ -854,10 +825,10 @@ \subsubsection{Example \label{subsec:tuples-Example8}\ref{subsec:tuples-Example8 \subparagraph{Solution} Iterating over a dictionary looks like iterating over a list of \lstinline!(key, value)! -pairs: +pairs. The result is converted to a \lstinline!Map! automatically. \begin{lstlisting} -dict.map { case (name, addr) => (addr, name) } // The result is converted to Map automatically. +dict.map { case (name, addr) => (addr, name) } // This has type Map[String, String]. \end{lstlisting} @@ -996,26 +967,21 @@ \subsection{Reasoning about type parameters in collections} it to a sequence using \lstinline!toSeq!. The type of the intermediate result after \lstinline!toSeq! is \lstinline!Seq[ (Int, Seq[String]) ]!, and the \lstinline!sortBy! operation is applied to that sequence. -So the sequence element matched by \lstinline!{ case (len, words) => len }! -is a tuple \lstinline!(Int, Seq[String])!, which means that the pattern -variables \lstinline!len! and \lstinline!words! must have types -\lstinline!Int! and \lstinline!Seq[String]! respectively. It would -be a type error to use the sorting key function \lstinline!{ case (len, words) => words }!: -the sorting key can be an integer \lstinline!len!, but not a string -sequence \lstinline!words! (because sorting by string sequences is -not automatically defined). +So, the sequence element matched by \lstinline!{ case (len, words) => len }! +is a tuple having the type \lstinline!(Int, Seq[String])!. Then the +pattern variables \lstinline!len! and \lstinline!words! must have +types \lstinline!Int! and \lstinline!Seq[String]! respectively. If we visualize how the type of the sequence should change at every step, we can more quickly understand how to implement the required task. Begin by writing down the intermediate types that would be needed during the computation: \begin{lstlisting} -words: Seq[String] // Need to group by word length. The result of groupBy() has type: - Map[Int, Seq[String]] // Need to sort by word length; cannot sort a dictionary! - // Need to convert this dictionary to a sequence: - Seq[ (Int, Seq[String]) ] // Now sort this by the `Int` value. Sorting does not change the types. - // It remains to swap the parts of all tuples in the sequence: - Seq[ (Seq[String], Int) ] // We are done. +words: Seq[String] // After groupBy() by word length, will have type: +Map[Int, Seq[String]] // To sort by word length, convert to a sequence: +Seq[ (Int, Seq[String]) ] // Sort by the `Int` value; type is unchanged: +Seq[ (Int, Seq[String]) ] // It remains to swap the parts of the tuples: +Seq[ (Seq[String], Int) ] // We are done. \end{lstlisting} Having written down these types, we are better assured that the computation can be done correctly. Writing the code becomes straightforward, since @@ -1032,14 +998,15 @@ \subsection{Exercises: Tuples and collections\index{exercises}} \subsubsection{Exercise \label{tuples-Exercise-1}\ref{tuples-Exercise-1}} -Find all pairs $i,j$ within $\left(0,1,...,9\right)$ such that $i+4*j>i*j$. +Find all integer pairs $i,j$ where $0\leq i\leq9$ and $0\leq j\leq9$ +and $i+4*j>i*j$. Hint: use \lstinline!flatMap! and \lstinline!filter!. \subsubsection{Exercise \label{tuples-Exercise-2}\ref{tuples-Exercise-2}} -Same task as in Exercise~\ref{tuples-Exercise-1}, but for $i,j,k$ -and the condition $i+4*j+9*k>i*j*k$. +Find all integer triples $i,j,k$ where $0\leq i\leq9$, $0\leq j\leq9$, +$0\leq k\leq9$, and $i+4*j+9*k>i*j*k$. \subsubsection{Exercise \label{tuples-Exercise-3}\ref{tuples-Exercise-3}} @@ -1053,10 +1020,11 @@ \subsubsection{Exercise \label{tuples-Exercise-3}\ref{tuples-Exercise-3}} \subsubsection{Exercise \label{tuples-Exercise-4}\ref{tuples-Exercise-4}} Convert a \lstinline!Seq[Int]! into a \lstinline!Seq[(Int, Boolean)]! -where the \lstinline!Boolean! value is \lstinline!true! when the -element is followed by a larger value. For example, the input sequence -\lstinline!Seq(1,3,2,4)! is to be converted into \lstinline!Seq((1,true),(3,false),(2,true),(4,false))!. -(The last element, 4, has no following element.) +where the \lstinline!Boolean! value is \lstinline!true! if an \lstinline!Int! +value is followed by a larger value. For example, the input \lstinline!Seq(1, 3, 2, 4)! +must be converted into \lstinline!Seq((1,true),(3,false),(2,true),(4,false))!. +The last value (here, 4) has no following value and is always paired +with \lstinline!false!. \subsubsection{Exercise \label{tuples-Exercise-7}\ref{tuples-Exercise-7}} @@ -1070,11 +1038,11 @@ \subsubsection{Exercise \label{tuples-Exercise-8}\ref{tuples-Exercise-8}} Write the solution of Exercise~\ref{tuples-Exercise-7} as a function with type parameter \lstinline!A! instead of the fixed type \lstinline!String!. -The required type signature and a sample test: +The type signature and a sample test: \begin{lstlisting} def reorder[A](p: Seq[A], q: Seq[Int]): Seq[A] = ??? // In Scala, ??? means "not yet implemented". -scala> reorder(Seq(6.0,2.0,8.0,4.0), Seq(20,10,40,30)) // Test with type parameter A = Double. +scala> reorder(Seq(6.0,2.0,8.0,4.0), Seq(20,10,40,30)) // Test with A = Double. res0: Seq[Double] = List(2.0, 6.0, 4.0, 8.0) \end{lstlisting} @@ -1090,8 +1058,9 @@ \subsubsection{Exercise \label{tuples-Exercise-6}\ref{tuples-Exercise-6}} Write the solution of Exercise~\ref{tuples-Exercise-5} as a function with type parameters \lstinline!P! and \lstinline!Q! instead of -the fixed types \lstinline!String! and \lstinline!Int!. The return -type of the function should be \lstinline!Map[Q, P]!. Run some tests +the fixed types \lstinline!Int! and \lstinline!String!. The function\textsf{'}s +arguments should be of types \lstinline!Seq[Q]! and \lstinline!Seq[P]!, +and the return type should be \lstinline!Map[P, Q]!. Run some tests using types \lstinline!P = Double! and \lstinline!Q = Set[Boolean]!. \subsubsection{Exercise \label{tuples-Exercise-9}\ref{tuples-Exercise-9}} @@ -1102,15 +1071,24 @@ \subsubsection{Exercise \label{tuples-Exercise-9}\ref{tuples-Exercise-9}} \begin{lstlisting} Seq(("apple", 2), ("pear", 3), ("apple", 5), ("lemon", 2), ("apple", 3)) \end{lstlisting} -the output must be \lstinline!Map("apple" -> 10, "pear" -> 3, "lemon" -> 2)!. +the output must be: \lstinline!Map("apple" -> 10, "pear" -> 3, "lemon" -> 2)!. Hint: use \lstinline!groupBy!, \lstinline!map!, \lstinline!sum!. \subsubsection{Exercise \label{tuples-Exercise-10}\ref{tuples-Exercise-10}} -Given a \lstinline!Seq[List[Int]]!, compute a new \lstinline!Seq[List[Int]]! -where each new inner list contains $3$ largest elements from the -old inner list (or fewer than $3$ if the old inner list is shorter). +Given a \lstinline!Seq[Seq[Int]]!, compute a new \lstinline!Seq[Seq[Int]]! +where each new inner sequence contains the $3$ largest elements from +the corresponding old inner sequence, sorted in descending order (or +fewer than $3$ elements if the old inner sequence is shorter). So, +for the input: +\begin{lstlisting} +Seq(Seq(0, 50, 5, 10, 30), Seq(10, 100), Seq(1, 2, 200, 20)) +\end{lstlisting} +the output must be: +\begin{lstlisting} +Seq(Seq(50, 30, 10), Seq(100, 10), Seq(200, 20, 2)) +\end{lstlisting} Hint: use \lstinline!map!, \lstinline!sortBy!, \lstinline!take!. @@ -1120,8 +1098,8 @@ \subsubsection{Exercise \label{tuples-Exercise-11}\ref{tuples-Exercise-11}} compute a set of type \lstinline!Set[(Int, Int)]! as the \textbf{Cartesian product}\index{Cartesian product} of the sets \lstinline!p! and \lstinline!q!. This is the set of all pairs \lstinline!(x, y)! where -\lstinline!x! is an element from the set \lstinline!p! and \lstinline!y! -is an element from the set \lstinline!q!. +\lstinline!x! is an element from \lstinline!p! and \lstinline!y! +is an element from \lstinline!q!. \textbf{(b)} Implement this computation as a function with type parameters \lstinline!I!, \lstinline!J! instead of \lstinline!Int!. The required @@ -1135,7 +1113,7 @@ \subsubsection{Exercise \label{tuples-Exercise-11}\ref{tuples-Exercise-11}} Hint: use \lstinline!flatMap! and \lstinline!map! on sets. -\subsubsection{Exercise \label{tuples-Exercise-12}\ref{tuples-Exercise-12}{*}} +\subsubsection{Exercise \label{tuples-Exercise-12}\ref{tuples-Exercise-12}} Given a \lstinline!Seq[Map[Person, Amount]]!, showing the amounts various people paid on each day, compute a \lstinline!Map[Person, Seq[Amount]]!, @@ -1144,8 +1122,8 @@ \subsubsection{Exercise \label{tuples-Exercise-12}\ref{tuples-Exercise-12}{*}} and a sample test: \begin{lstlisting}[mathescape=true] def payments[Person, Amount](data: Seq[Map[Person, Amount]]): Map[Person, Seq[Amount]] = ??? -// On day 1, Tarski paid 10 and G$\text{\color{dkgreen}\"o}$del paid 20. On day 2, Church paid 100 and Gentzen paid 50, etc. -scala> payments(Seq(Map("Tarski" -> 10, "G$\text{\color{mauve}\"o}$del" -> 20), Map("Church" -> 100, "Gentzen" -> 50), Map("Tarski" -> 50), Map("Banach" -> 15, "Gentzen" -> 35))) +// On day 1, Tarski paid 10 and G$\text{\color{dkgreen}\"o}$del paid 20. On day 2, Gentzen paid 50, etc. +scala> payments(Seq(Map("Tarski" -> 10, "G$\text{\color{mauve}\"o}$del" -> 20), Map("Gentzen" -> 50), Map("Tarski" -> 50, "Church" -> 100), Map("Banach" -> 15, "Gentzen" -> 35))) res0: Map[String, Seq[Int]] = Map(Genzten -> List(50, 35), Church -> List(100), Banach -> List(15), Tarski -> List(10, 50), G$\text{\"o}$del -> List(20)) \end{lstlisting} @@ -1156,7 +1134,7 @@ \section{Converting a sequence into a single value} Until this point, we have been working with sequences using methods such as \lstinline!map! and \lstinline!zip!. These techniques are -powerful but still insufficient for solving certain problems. +powerful but still insufficient for certain tasks. A simple computation that is impossible to do using \lstinline!map! is obtaining the sum of a sequence of numbers. The standard library @@ -1173,7 +1151,7 @@ \section{Converting a sequence into a single value} value, such that we could ourselves implement \lstinline!sum!, \lstinline!count!, \lstinline!max!, and other similar computations. -Another task not solvable with \lstinline!map!, \lstinline!sum!, +Another task not easily solved with \lstinline!map!, \lstinline!sum!, etc., is to compute a floating-point number from a given sequence of decimal digits (including a \textsf{``}dot\textsf{''} character): \begin{lstlisting} @@ -1182,9 +1160,7 @@ \section{Converting a sequence into a single value} scala> digitsToDouble(Seq('2', '0', '4', '.', '5')) res0: Double = 204.5 \end{lstlisting} -Why is it impossible to implement this function by using \lstinline!map!, -\lstinline!sum!, \lstinline!zip!, and other methods we have seen -so far? In fact, the same task for integer numbers (instead of floating-point +Note that the same task for integer numbers (instead of floating-point numbers) \emph{can} be implemented via \lstinline!length!, \lstinline!map!, \lstinline!sum!, and \lstinline!zip!: \begin{lstlisting} @@ -1267,7 +1243,7 @@ \subsection{Inductive definitions of aggregation functions\label{subsec:Inductiv \paragraph{The method \texttt{length}} -Base case: The length of an empty sequence is zero, so \lstinline!Seq().length == 0!. +Base case: The length of an empty sequence is zero, so we write: \lstinline!Seq().length == 0!. Inductive step: if \lstinline!xs.length! is known then \lstinline!(x +: xs).length == xs.length + 1!. @@ -1305,10 +1281,12 @@ \subsection{Inductive definitions of aggregation functions\label{subsec:Inductiv verify that, we use the associativity law of the concatenation operation (to be proved in Statement~\ref{subsec:Statement-concat-array-associativity}), which allows us to write: \lstinline!(x +: xs) ++ ys == x +: (xs ++ ys)!. -Then we get: +Then: \begin{lstlisting} -((x +: xs) ++ ys).length == (x +: (xs ++ ys)).length == 1 + (xs.length + ys.length) - == (x +: xs).length + ys.length // Right-hand side of the property for (x +: xs) +((x +: xs) ++ ys).length // Expect to equal (x +: xs).length + ys.length + == (x +: (xs ++ ys)).length + == 1 + (xs.length + ys.length) + == (x +: xs).length + ys.length \end{lstlisting} In this way, we show that the property holds for \lstinline!x +: xs! and \lstinline!ys! assuming it holds for \lstinline!xs! and \lstinline!ys!. @@ -1345,10 +1323,7 @@ \subsection{Implementing functions by recursion} to decide whether we have the base case or the inductive step. As an example, let us define \lstinline!sum! by recursion. The base case returns \lstinline!0!, while the inductive step returns a value -computed from the recursive call. In this example, - -\begin{wrapfigure}{l}{0.53\columnwidth}% -\vspace{-0.85\baselineskip} +computed from the recursive call. Look at this code: \begin{lstlisting} def sum(s: Seq[Int]): Int = if (s.isEmpty) 0 else { val x = s.head // To split s = x +: xs, compute x @@ -1356,16 +1331,12 @@ \subsection{Implementing functions by recursion} sum(xs) + x // Call sum(...) recursively. } \end{lstlisting} - -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent the \lstinline!if/else! expression will separate the base -case from the inductive step. In the inductive step, it is convenient -to split the given sequence \lstinline!s! into its first element -\lstinline!x!, or the \textsf{``}head\textsf{''} of \lstinline!s!, and the remainder -(\textsf{``}tail\textsf{''}) sequence \lstinline!xs!. So, we split \lstinline!s! -as \lstinline!s = x +: xs! rather than as \lstinline!s = xs :+ x!.\footnote{It is easier to remember the meaning of \lstinline!x +: xs! and \lstinline!xs :+ x! +The \lstinline!if/else! expression separates the base case from the +inductive step. In the inductive step, it is convenient to split the +given sequence \lstinline!s! into its first element \lstinline!x!, +or the \textsf{``}head\textsf{''} of \lstinline!s!, and the remainder (\textsf{``}tail\textsf{''}) +sequence \lstinline!xs!. So, we split \lstinline!s! as \lstinline!s = x +: xs! +rather than as \lstinline!s = xs :+ x!.\footnote{It is easier to remember the meaning of \lstinline!x +: xs! and \lstinline!xs :+ x! if we note that the \emph{col}on (\lstinline!:!) always points to the \emph{col}lection (\lstinline!xs!) and the plus sign (\lstinline!+!) to a single element (\lstinline!x!) that is being added.} @@ -1378,9 +1349,6 @@ \subsection{Implementing functions by recursion} Let us implement \lstinline!digitsToInt! according to the inductive definition shown in Section~\ref{subsec:Inductive-definitions-of-aggregation-functions}: - -\begin{wrapfigure}{l}{0.64\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} def digitsToInt(s: Seq[Int]): Int = if (s.isEmpty) 0 else { val x = s.last // To split s = xs :+ x, compute x @@ -1388,16 +1356,12 @@ \subsection{Implementing functions by recursion} digitsToInt(xs) * 10 + x // Call digitsToInt(...) recursively. } \end{lstlisting} - -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent In this example, it is important to split the sequence \lstinline!s! +In this example, it is important to split the sequence \lstinline!s! into \lstinline!xs :+ x! and not into \lstinline!x +: xs!. The reason is that digits increase their numerical value from right to left, so the correct result is computed if we split \lstinline!s! into \lstinline!xs :+ x! and multiply \lstinline!digitsToInt(xs)! by -$10$. +$10$ before adding \lstinline!x!. These examples show how mathematical induction is converted into recursive code. This approach often works but has two technical problems. The @@ -1441,33 +1405,26 @@ \subsection{Tail recursion\label{subsec:Tail-recursion}} The problem is with the code of the function \lstinline!lengthS!. This function calls itself \emph{inside} the expression \lstinline!1 + lengthS(...)!. So, we can visualize how the computer evaluates that code: - -\begin{wrapfigure}{l}{0.4275\columnwidth}% -\vspace{-1\baselineskip} \begin{lstlisting} lengthS(Seq(1, 2, ..., 100000)) = 1 + lengthS(Seq(2, ..., 100000)) = 1 + (1 + lengthS(Seq(3, ..., 100000))) = ... \end{lstlisting} - -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent The code of \lstinline!lengthS! will repeat the inductive -step, that is, the \textsf{``}\lstinline!else!\textsf{''} part of the \textsf{``}\lstinline!if/else!\textsf{''}, -about $100$ thousand times. Each time, the sub-expression with nested -computations \lstinline!1+(1+(...))! will get larger. This intermediate -sub-expression needs to be held somewhere in memory until the function -body goes into the base case, with no more recursive calls. When that -happens, the intermediate sub-expression will contain about $100$ -thousand nested function calls still waiting to be evaluated. A special +The code of \lstinline!lengthS! will repeat the inductive step, that +is, the \textsf{``}\lstinline!else!\textsf{''} part of the \textsf{``}\lstinline!if/else!\textsf{''}, +about $100$ thousand times. Each time, the intermediate sub-expression +with nested computations \lstinline!1 + (1 + (...))! will get larger. +That sub-expression needs to be held somewhere in memory until the +function body goes into the base case, with no more recursive calls. +When that happens, the intermediate sub-expression will contain about +$100000$ nested function calls still waiting to be evaluated. A special area of memory called \textbf{\index{stack memory}stack memory} is -dedicated to storing the data for all not-yet-evaluated nested function -calls. Due to the way computer memory is managed, the stack memory -has a fixed size and cannot grow automatically. So, when the intermediate -expression becomes large enough, it causes an overflow of the stack -memory and crashes the program. +dedicated to storing the arguments for all not-yet-evaluated nested +function calls. Due to the way computer memory is managed, the stack +memory has a fixed size and cannot grow automatically. So, when the +intermediate expression becomes large enough, it causes an overflow +of the stack memory and crashes the program. One way to avoid stack overflows is to use a trick called \textbf{tail recursion\index{tail recursion}}. Using tail recursion means rewriting @@ -1476,21 +1433,14 @@ \subsection{Tail recursion\label{subsec:Tail-recursion}} call must be \emph{itself} the last computation in the function body, rather than placed inside other computations. Here is an example of tail-recursive code: - -\begin{wrapfigure}{l}{0.42\columnwidth}% -\vspace{-1.1\baselineskip} \begin{lstlisting} def lengthT(s: Seq[Int], res: Int): Int = if (s.isEmpty) res else lengthT(s.tail, res + 1) \end{lstlisting} - -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent In this code, one of the branches of the \lstinline!if/else! -returns a fixed value without doing any recursive calls, while the -other branch returns the result of a recursive call to \lstinline!lengthT(...)!. +In this code, one of the branches of the \lstinline!if/else! returns +a fixed value without doing any recursive calls, while the other branch +returns the result of a recursive call to \lstinline!lengthT(...)!. In the code of \lstinline!lengthT!, recursive calls never occur within any sub-expressions. @@ -1513,14 +1463,12 @@ \subsection{Tail recursion\label{subsec:Tail-recursion}} looks like this: \begin{lstlisting} import scala.annotation.tailrec + @tailrec def lengthT(s: Seq[Int], res: Int): Int = if (s.isEmpty) res else lengthT(s.tail, res + 1) \end{lstlisting} Let us trace the evaluation of this function on an example: - -\begin{wrapfigure}{l}{0.565\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} lengthT(Seq(1, 2, 3), 0) = lengthT(Seq(2, 3), 0 + 1) // = lengthT(Seq(2, 3), 1) @@ -1529,9 +1477,6 @@ \subsection{Tail recursion\label{subsec:Tail-recursion}} = 3 \end{lstlisting} -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - \noindent All sub-expressions such as \lstinline!1 + 1! and \lstinline!2 + 1! are computed \emph{before} recursive calls to \lstinline!lengthT!. Because of that, sub-expressions do not grow within the stack memory. @@ -1560,12 +1505,15 @@ \subsection{Tail recursion\label{subsec:Tail-recursion}} for the accumulator is \lstinline!0!, since in the base case (an empty sequence \lstinline!s!) we need to return \lstinline!0!. -So, a tail-recursive implementation of \lstinline!lengthT! requires -us to define \emph{two} functions: the tail-recursive \lstinline!lengthT! -and the main function that will set the initial value of the accumulator -argument. To emphasize that \lstinline!lengthT! is a helper function, -one could define it \emph{inside} the main function: +It appears useful to define the helper function (\lstinline!lengthT!) +separately. Then \lstinline!length! will just call \lstinline!lengthT! +and specify the initial value of the accumulator argument. To emphasize +that \lstinline!lengthT! is a helper function that is only used by +\lstinline!length! to achieve tail recursion, we define \lstinline!lengthT! +as a nested function inside the code of \lstinline!length!: \begin{lstlisting} +import scala.annotation.tailrec + def length[A](xs: Seq[A]): Int = { @tailrec def lengthT(s: Seq[A], res: Int): Int = { if (s.isEmpty) res @@ -1591,7 +1539,8 @@ \subsection{Tail recursion\label{subsec:Tail-recursion}} \begin{lstlisting} def f(x: Int, y: Boolean = false): Int = ... // Function body. \end{lstlisting} -is equivalent to defining two functions (with the same name): +is equivalent to defining two functions with the same name but different +numbers of arguments: \begin{lstlisting} def f(x: Int, y: Boolean) = ... // Define the function body here. def f(x: Int): Int = f(Int, false) // Call the function defined above. @@ -1621,11 +1570,10 @@ \subsection{Tail recursion\label{subsec:Tail-recursion}} obey a suitable associativity law. Even if a code rearrangement exists, it may not be immediately obvious how to find it. -As an example, let us implement a tail-recursive version of the function -\lstinline!digitsToInt! from the previous subsection, where the recursive -call is within a sub-expression \lstinline!digitsToInt(xs) * 10 + x!. -To transform the code into a tail-recursive form, we need to rearrange -the main computation: +An example is a tail-recursive version of the function \lstinline!digitsToInt! +from the previous subsection, where the sub-expression \lstinline!digitsToInt(xs) * 10 + x! +was a non-tail-recursive call. To transform the code into a tail-recursive +form, we need to rearrange the main computation: \[ r=d_{n-1}+10*\left(d_{n-2}+10*\left(d_{n-3}+10*\left(...+10*d_{0}\right)\right)\right)\quad, \] @@ -1637,9 +1585,6 @@ \subsection{Tail recursion\label{subsec:Tail-recursion}} It follows that the digit sequence \lstinline!s! must be split into the \emph{leftmost} digit and the rest, \lstinline!s == s.head +: s.tail!. So, a tail-recursive implementation of the above formula is: - -\begin{wrapfigure}{l}{0.57\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} @tailrec def fromDigits(s: Seq[Int], res: Int = 0):Int = // `res` is the accumulator. @@ -1649,11 +1594,7 @@ \subsection{Tail recursion\label{subsec:Tail-recursion}} scala> fromDigits(Seq(1, 2, 3, 4)) res0: Int = 1234 \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent Despite a similarity between this code and the code of \lstinline!digitsToInt! +Despite a similarity between this code and the code of \lstinline!digitsToInt! from the previous subsection, the implementation of \lstinline!fromDigits! cannot be directly derived from the inductive definition of \lstinline!digitsToInt!. We need a separate proof that \lstinline!fromDigits(s, 0)! computes @@ -1827,11 +1768,11 @@ \subsection{Implementing general aggregation (\texttt{foldLeft})\label{subsec:im the evaluation of this function when applied to \lstinline!Seq(1, 2, 3)!: \begin{lstlisting} sum(Seq(1, 2, 3)) == leftFold(Seq(1, 2, 3), 0, g) - // Here, g = (x, y) => x + y, so g(x, y) = x + y. - == leftFold(Seq(2, 3), g(0, 1), g) // g (0, 1) = 1. - == leftFold(Seq(2, 3), 1, g) // Now expand the code of `leftFold`. - == leftFold(Seq(3), g(1, 2), g) // g(1, 2) = 3; expand the code. - == leftFold(Seq(), g(3, 3), g) // g(3, 3) = 6; expand the code. + // Here, g = (x, y) => x + y, so g(x, y) = x + y. + == leftFold(Seq(2, 3), g(0, 1), g) // g (0, 1) = 1. + == leftFold(Seq(2, 3), 1, g) // Now expand the code of `leftFold`. + == leftFold(Seq(3), g(1, 2), g) // g(1, 2) = 3; expand the code. + == leftFold(Seq(), g(3, 3), g) // g(3, 3) = 6; expand the code. == 6 \end{lstlisting} The second argument of \lstinline!leftFold! is the accumulator argument. @@ -1876,21 +1817,14 @@ \subsection{Implementing general aggregation (\texttt{foldLeft})\label{subsec:im In general, the type of the accumulator value can be different from the type of the sequence elements. An example is an implementation of \lstinline!count!: - -\begin{wrapfigure}{l}{0.55\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} def count[A](s: Seq[A], p: A => Boolean): Int = s.foldLeft(0) { (x, y) => x + (if (p(y)) 1 else 0) } \end{lstlisting} - -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -\noindent The accumulator is of type \lstinline!Int!, while the sequence -elements can have an arbitrary type, parameterized by \lstinline!A!. -The \lstinline!foldLeft! method works in the same way for all types -of accumulators and all types of sequence elements. +The accumulator has type \lstinline!Int!, while the sequence elements +can have an arbitrary type, parameterized by \lstinline!A!. The \lstinline!foldLeft! +method works in the same way for all types of accumulators and all +types of sequence elements. Since \lstinline!foldLeft! is tail-recursive, stack overflows will not occur even with long sequences. The method \lstinline!foldLeft! @@ -1904,7 +1838,7 @@ \subsection{Implementing general aggregation (\texttt{foldLeft})\label{subsec:im focus on \lstinline!foldLeft! because the other fold-like operations are similar. -\subsection{Solved examples: using \texttt{foldLeft}\index{solved examples}} +\subsection{Examples: Using \texttt{foldLeft}\index{examples (with code)}} \subsubsection{Example \label{subsec:Example-1-max-foldleft}\ref{subsec:Example-1-max-foldleft}} @@ -1918,17 +1852,17 @@ \subsubsection{Example \label{subsec:Example-1-max-foldleft}\ref{subsec:Example- function for sequences. Base case: For an empty sequence, return \lstinline!Int.MinValue!. Inductive step: If \lstinline!max! is already computed on a sequence \lstinline!xs!, say \lstinline!max(xs) = b!, the value of \lstinline!max! -on a sequence \lstinline!xs :+ x! is \lstinline!math.max(b, x)!. -So, the code is: +on a sequence \lstinline!xs :+ x! is the maximum of \lstinline!b! +and \lstinline!x!. So, the code is: \begin{lstlisting} -def max(s: Seq[Int]): Int = s.foldLeft(Int.MinValue) { (b, x) => math.max(b, x) } +def max(s: Seq[Int]): Int = s.foldLeft(Int.MinValue) { (b, x) => if (b > x) b else x } \end{lstlisting} If we are sure that the function will never be called on empty sequences, we can implement \lstinline!max! in a simpler way by using the \lstinline!reduce! method: \begin{lstlisting} -def max(s: Seq[Int]): Int = s.reduce { (x, y) => math.max(x, y) } +def max(s: Seq[Int]): Int = s.reduce { (x, y) => if (y > x) y else x } \end{lstlisting} @@ -1955,11 +1889,11 @@ \subsubsection{Example \label{subsec:Example-3-digitstoint-foldleft-1}\ref{subse value? We need to accumulate intermediate values of \emph{all four} numbers (\lstinline!min!, \lstinline!max!, \lstinline!sum!, and \lstinline!length!) in a tuple. So the required type of the accumulator -is \lstinline!(Double, Double, Double, Double)!. To avoid repeating +is \lstinline!(Double, Double, Double, Int)!. To avoid repeating a long type expression, we can define a type alias\index{type alias} for it, say, \lstinline!D4!: \begin{lstlisting} -scala> type D4 = (Double, Double, Double, Double) +scala> type D4 = (Double, Double, Double, Int) defined type alias D4 \end{lstlisting} The updater updates each of the four numbers according to the definitions @@ -1972,7 +1906,7 @@ \subsubsection{Example \label{subsec:Example-3-digitstoint-foldleft-1}\ref{subse Now we can write the code of the required function: \begin{lstlisting} def f(xs: Seq[Double]): (Double, Double, Double) = { - val init: D4 = (Double.PositiveInfinity, Double.NegativeInfinity, 0, 0) + val init: D4 = (Double.PositiveInfinity, Double.NegativeInfinity, 0.0, 0) val (min, max, sum, length) = xs.foldLeft(init)(update) (min, max, sum/length) } @@ -2029,30 +1963,28 @@ \subsubsection{Example \label{subsec:Example-3-digitstoint-foldleft}\ref{subsec: \end{lstlisting} -\subsubsection{Example \label{subsec:Example-4-digitstodouble-foldleft}\ref{subsec:Example-4-digitstodouble-foldleft}{*}} +\subsubsection{Example \label{subsec:Example-4-digitstodouble-foldleft}\ref{subsec:Example-4-digitstodouble-foldleft}} Implement the function \lstinline!digitsToDouble! using \lstinline!foldLeft!. -The argument is of type \lstinline!Seq[Char]!. As a test, the expression -\lstinline!digitsToDouble(Seq('3','4','.','2','5'))! must evaluate -to \lstinline!34.25!. Assume that all input characters are either -digits or a dot (so, negative numbers are not supported). +The argument is of type \lstinline!Seq[Char]!. As a test, \lstinline!digitsToDouble(Seq('3','4','.','2','5'))! +must evaluate to \lstinline!34.25!. Assume that all input characters +are either digits or a dot (so, negative numbers are not supported). \subparagraph{Solution} The evaluation of a \lstinline!foldLeft! on a sequence of digits will visit the sequence from left to right. The updating function -should work the same as in \lstinline!digitsToInt! until a dot character -is found. After that, we need to change the updating function. So, -we need to remember whether a dot character has been seen. The only -way for \lstinline!foldLeft! to \textsf{``}remember\textsf{''} any data is to hold -that data in the accumulator value. We can choose the type of the -accumulator according to our needs. So, for this task we can choose -the accumulator to be a \emph{tuple} that contains, for instance, -the floating-point result constructed so far and a \lstinline!Boolean! -flag showing whether we have already seen the dot character. - -To see what \lstinline!digitsToDouble! must do, let us consider how -the evaluation of \lstinline!digitsToDouble(Seq('3', '4', '.', '2', '5'))! +should work as in \lstinline!digitsToInt! until a dot character is +found. After that, we need to change the updating function. So, we +need to remember whether a dot character has been seen. The only way +for \lstinline!foldLeft! to \textsf{``}remember\textsf{''} any data is to hold that +data in the accumulator value. We can choose the type of the accumulator +according to our needs. So, for this task we can choose the accumulator +to be a \emph{tuple} that contains, for instance, the floating-point +result constructed so far and a \lstinline!Boolean! flag showing +whether we have already seen the dot character. + +Let us consider how the evaluation of \lstinline!digitsToDouble(Seq('3', '4', '.', '2', '5'))! should go. We can write a table showing the intermediate result at each iteration. This will hopefully help us figure out what the accumulator and the updater function $g(...)$ must be: @@ -2062,15 +1994,15 @@ \subsubsection{Example \label{subsec:Example-4-digitstodouble-foldleft}\ref{subs \textbf{\small{}Current digit $c$} & \textbf{\small{}Previous result $n$} & \textbf{\small{}New result $n'=g(n,c)$}\tabularnewline \hline \hline -\lstinline!'3'! & {\small{}$0.0$} & {\small{}$3.0$}\tabularnewline +\lstinline!'3'! & {\footnotesize{}$0.0$} & {\footnotesize{}$3.0$}\tabularnewline \hline -\lstinline!'4'! & {\small{}$3.0$} & {\small{}$34.0$}\tabularnewline +\lstinline!'4'! & {\footnotesize{}$3.0$} & {\footnotesize{}$34.0$}\tabularnewline \hline -\lstinline!'.'! & {\small{}$34.0$} & {\small{}$34.0$}\tabularnewline +\lstinline!'.'! & {\footnotesize{}$34.0$} & {\footnotesize{}$34.0$}\tabularnewline \hline -\lstinline!'2'! & {\small{}$34.0$} & {\small{}$34.2$}\tabularnewline +\lstinline!'2'! & {\footnotesize{}$34.0$} & {\footnotesize{}$34.2$}\tabularnewline \hline -\lstinline!'5'! & {\small{}$34.2$} & {\small{}$34.25$}\tabularnewline +\lstinline!'5'! & {\footnotesize{}$34.2$} & {\footnotesize{}$34.25$}\tabularnewline \hline \end{tabular} \par\end{center} @@ -2083,8 +2015,8 @@ \subsubsection{Example \label{subsec:Example-4-digitstodouble-foldleft}\ref{subs be defined by: \[ g(n,c)=\begin{cases} -n*10+c\quad, & \text{if the digit is before the dot character.}\\ -n+c/f\quad, & \text{if after the dot character, where }f=10,100,1000,...\text{ for each new digit.} +n*10+c & \text{if the digit is before the dot}\\ +n+c/f & \text{if after the dot, where }f=10,100,1000,...\text{ for each new digit} \end{cases} \] @@ -2106,18 +2038,15 @@ \subsubsection{Example \label{subsec:Example-4-digitstodouble-foldleft}\ref{subs \begin{lstlisting} def update(acc: (Double, Boolean, Double), c: Char): (Double, Boolean, Double) = acc match { case (num, flag, factor) => - if (c == '.') (num, true, factor) // Set flag to `true` after a dot character was seen. + if (c == '.') (num, true, factor) // Set flag to `true` after seeing a dot. else { val digit = c - '0' - if (flag) (num + digit / factor, flag, factor * 10) // This digit is after the dot. - else (num * 10 + digit, flag, factor) // This digit is before the dot. + if (flag) (num + digit / factor, flag, factor * 10) // After the dot. + else (num * 10 + digit, flag, factor) // Before the dot. } } \end{lstlisting} Now we can implement \lstinline!digitsToDouble! like this: - -\begin{wrapfigure}{l}{0.48\columnwidth}% -\vspace{-0.5\baselineskip} \begin{lstlisting} def digitsToDouble(d: Seq[Char]): Double = { val initAcc = (0.0, false, 10.0) @@ -2125,20 +2054,15 @@ \subsubsection{Example \label{subsec:Example-4-digitstodouble-foldleft}\ref{subs num } -scala> digitsToDouble(Seq('3','4','.','2','5')) +scala> digitsToDouble(Seq('3', '4', '.', '2', '5')) res0: Double = 34.25 \end{lstlisting} - -\vspace{-1.2\baselineskip} -\end{wrapfigure}% - -\noindent The result of calling \lstinline!d.foldLeft! is a tuple -\lstinline!(num, flag, factor)!, in which only the first part, \lstinline!num!, -is needed. In Scala\textsf{'}s pattern matching syntax, the underscore (\lstinline!_!) -denotes pattern variables whose values are not needed in the code. -We could get the first part using the accessor method \lstinline!._1!, -but the code will be more readable if we show all parts of the tuple -\lstinline!(num, _, _)!. +The result of calling \lstinline!d.foldLeft! is a tuple \lstinline!(num, flag, factor)!, +in which only the first part, \lstinline!num!, is needed. In Scala\textsf{'}s +pattern matching syntax, the underscore (\lstinline!_!) denotes pattern +variables whose values are not needed in the code. We could get the +first part using the accessor method \lstinline!._1!, but the code +will be more readable if we show all parts of the tuple \lstinline!(num, _, _)!. \subsubsection{Example \label{subsec:Example-foldleft-7}\ref{subsec:Example-foldleft-7}} @@ -2217,25 +2141,17 @@ \subsubsection{Example \label{subsec:Example-foldleft-7}\ref{subsec:Example-fold } \end{lstlisting} This code shows examples of partial functions that are applied safely. -One of these partial functions is used in the expression - -\begin{wrapfigure}{l}{0.3\columnwidth}% -\vspace{-0.95\baselineskip} +One of these partial functions is used in this sub-expression: \begin{lstlisting} holdover match { case Seq() => ... case Seq(a) => ... } \end{lstlisting} - -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent This code works when \lstinline!holdover! is empty or has -length $1$ but fails for longer sequences. In the implementation -of \lstinline!toPairs!, the value of \lstinline!holdover! will always -be a sequence of length at most $1$, so it is safe to use this partial -function. +This code works when \lstinline!holdover! is empty or has length +$1$ but fails for longer sequences. In the implementation of \lstinline!toPairs!, +the value of \lstinline!holdover! will always be a sequence of length +at most $1$, so it is safe to use this partial function. \subsection{Exercises: Using \texttt{foldLeft}} @@ -2281,7 +2197,9 @@ \subsubsection{Exercise \label{subsec:Exercise-2.2-foldleft-4}\ref{subsec:Exerci Use \lstinline!foldLeft! to implement a function \lstinline!filterMap! that combines \lstinline!map! and \lstinline!filter! for sequences. -The required type signature and a sample test: +The predicate is applied to the elements of the initial sequence, +and values that pass the predicate are mapped. The required type signature +and a sample test: \begin{lstlisting} def filterMap[A, B](xs: Seq[A])(pred: A => Boolean)(f: A => B): Seq[B] = ??? @@ -2290,13 +2208,12 @@ \subsubsection{Exercise \label{subsec:Exercise-2.2-foldleft-4}\ref{subsec:Exerci \end{lstlisting} -\subsubsection{Exercise \label{subsec:Exercise-2.2-foldleft-5}\ref{subsec:Exercise-2.2-foldleft-5}{*}} +\subsubsection{Exercise \label{subsec:Exercise-2.2-foldleft-5}\ref{subsec:Exercise-2.2-foldleft-5}} -Split a sequence into subsequences (\textsf{``}batches\textsf{''}) of length not larger -than a given maximum length $n$. The required type signature and -a sample test: +Split a sequence into subsequences (\textsf{``}batches\textsf{''}) of length at most +$n$. The required type signature and a sample test: \begin{lstlisting} -def byLength[A](xs: Seq[A], length: Int): Seq[Seq[A]] = ??? +def byLength[A](xs: Seq[A], maxLength: Int): Seq[Seq[A]] = ??? scala> byLength(Seq("a", "b", "c", "d"), 2) res0: Seq[Seq[String]] = List(List(a, b), List(c, d)) @@ -2306,7 +2223,7 @@ \subsubsection{Exercise \label{subsec:Exercise-2.2-foldleft-5}\ref{subsec:Exerci \end{lstlisting} -\subsubsection{Exercise \label{subsec:Exercise-2.2-foldleft-5-1}\ref{subsec:Exercise-2.2-foldleft-5-1}{*}} +\subsubsection{Exercise \label{subsec:Exercise-2.2-foldleft-5-1}\ref{subsec:Exercise-2.2-foldleft-5-1}} Split a sequence into batches by \textsf{``}weight\textsf{''} computed via a given function. The total weight of items in any batch should not be larger @@ -2320,7 +2237,7 @@ \subsubsection{Exercise \label{subsec:Exercise-2.2-foldleft-5-1}\ref{subsec:Exer \end{lstlisting} -\subsubsection{Exercise \label{subsec:Exercise-2.2-foldleft-6}\ref{subsec:Exercise-2.2-foldleft-6}{*}} +\subsubsection{Exercise \label{subsec:Exercise-2.2-foldleft-6}\ref{subsec:Exercise-2.2-foldleft-6}} Use \lstinline!foldLeft! to implement a \lstinline!groupBy! function. The type signature and a test: @@ -2332,10 +2249,10 @@ \subsubsection{Exercise \label{subsec:Exercise-2.2-foldleft-6}\ref{subsec:Exerci \end{lstlisting} Hints: The accumulator should be of type \lstinline!Map[K, Seq[A]]!. -To work with dictionaries, you will need to use the methods \lstinline!getOrElse! -and \lstinline!updated!. The method \lstinline!getOrElse! fetches -a value from a dictionary by key, and returns the given default value -if the dictionary does not contain that key: +Use the methods \lstinline!updated! and \lstinline!getOrElse! to +work with dictionaries. The method \lstinline!getOrElse! fetches +a value from a dictionary by key but returns a default value if the +key is not in the dictionary: \begin{lstlisting} scala> Map("a" -> 1, "b" -> 2).getOrElse("a", 300) res0: Int = 1 @@ -2361,20 +2278,13 @@ \section{Converting a single value into a sequence\label{sec:ch2Converting-a-sin the opposite operation (\textsf{``}unfolding\textsf{''}) converts a single value into a sequence. An example of this task is to compute the sequence of decimal digits for a given integer: - -\begin{wrapfigure}{l}{0.37\columnwidth}% -\vspace{-1\baselineskip} \begin{lstlisting} def digitsOf(x: Int): Seq[Int] = ??? scala> digitsOf(2405) res0: Seq[Int] = List(2, 4, 0, 5) \end{lstlisting} - -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent We cannot implement \lstinline!digitsOf! using \lstinline!map!, +We cannot implement \lstinline!digitsOf! using \lstinline!map!, \lstinline!zip!, or \lstinline!foldLeft!, because these methods work only if we \emph{already have} a sequence; but the function \lstinline!digitsOf! needs to create a new sequence. We could create a sequence via the @@ -2395,24 +2305,17 @@ \section{Converting a single value into a sequence\label{sec:ch2Converting-a-sin generate a sequence of initially unknown length according to any given requirements. -The Scala library has a general stream-producing function \lstinline!Stream.iterate!.\footnote{In a future version of Scala 3, the \lstinline!Stream! class will -be replaced by \lstinline!LazyList!.} This function has two arguments, the initial value and a function +The Scala library has a general stream-producing function \lstinline!Stream.iterate!.\footnote{In Scala 3, the \lstinline!Stream! class is replaced by \lstinline!LazyList!.} +This function has two arguments, the initial value and a function that computes the next value from the previous one: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.7\baselineskip} \begin{lstlisting} scala> Stream.iterate(2) { x => x + 10 } res0: Stream[Int] = Stream(2, ?) \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent The stream is ready to start computing the next elements -of the sequence (so far, only the first element, \lstinline!2!, has -been computed). In order to see the next elements, we need to stop -the stream at a finite size and then convert the result to a list: +The stream is ready to start computing the next elements of the sequence +(so far, only the first element, \lstinline!2!, has been computed). +In order to see the next elements, we need to stop the stream at a +finite size and then convert the result to a list: \begin{lstlisting} scala> Stream.iterate(2) { x => x + 10 }.take(6).toList res1: List[Int] = List(2, 12, 22, 32, 42, 52) @@ -2422,10 +2325,9 @@ \section{Converting a single value into a sequence\label{sec:ch2Converting-a-sin the program will keep producing more elements until it runs out of memory and crashes. -Streams are similar to sequences, and methods such as \lstinline!map!, -\lstinline!filter!, and \lstinline!flatMap! are also defined for -streams. For instance, the method \lstinline!drop! skips a given -number of initial elements: +Streams have methods such as \lstinline!map!, \lstinline!filter!, +and \lstinline!flatMap! similar to sequences. For instance, the method +\lstinline!drop! skips a given number of initial elements: \begin{lstlisting} scala> Seq(10, 20, 30, 40, 50).drop(3) res2: Seq[Int] = List(40, 50) @@ -2450,11 +2352,8 @@ \section{Converting a single value into a sequence\label{sec:ch2Converting-a-sin \end{itemize} Here $\left\lfloor \frac{n_{k}}{10}\right\rfloor $ is the mathematical notation for the integer division by $10$. Let us tabulate the evaluation -of the sequence $n_{k}$ for $n=2405$: - -\begin{wraptable}{l}{0.47\columnwidth}% -\begin{centering} -\vspace{-0.85\baselineskip} +of the sequence $n_{k}$ for $n=2405$: +\begin{center} \begin{tabular}{|c|c|c|c|c|c|c|c|} \hline {\small{}$k=$} & {\small{}$0$} & {\small{}$1$} & {\small{}$2$} & {\small{}$3$} & {\small{}$4$} & {\small{}$5$} & {\small{}$6$}\tabularnewline @@ -2464,15 +2363,13 @@ \section{Converting a single value into a sequence\label{sec:ch2Converting-a-sin {\small{}$n_{k}\text{ mod }10=$} & {\small{}$5$} & {\small{}$0$} & {\small{}$4$} & {\small{}$2$} & {\small{}$0$} & {\small{}$0$} & {\small{}$0$}\tabularnewline \hline \end{tabular} -\par\end{centering} -\vspace{-1\baselineskip} -\end{wraptable}% - -\noindent The numbers $n_{k}$ will remain all zeros after $k=4$. -It is clear that the useful part of the sequence is before it becomes -all zeros. In this example, the sequence $n_{k}$ needs to be stopped -at $k=4$. The sequence of digits then becomes $\left[5,0,4,2\right]$, -and we need to reverse it to obtain $\left[2,4,0,5\right]$. For reversing +\par\end{center} + +The numbers $n_{k}$ will remain all zeros after $k=4$. It is clear +that the useful part of the sequence is before it becomes all zeros. +In this example, the sequence $n_{k}$ needs to be stopped at $k=4$. +The sequence of digits then becomes $\left[5,0,4,2\right]$, and we +need to reverse it to obtain $\left[2,4,0,5\right]$. For reversing a sequence, the Scala library has the standard method \lstinline!reverse!. So, a complete implementation for \lstinline!digitsOf! is: \begin{lstlisting} @@ -2484,7 +2381,7 @@ \section{Converting a single value into a sequence\label{sec:ch2Converting-a-sin .toList.reverse } \end{lstlisting} -We can shorten the code by using the syntax such as \lstinline!(_ % 10)! +We can shorten the code by using the syntax \lstinline!(_ % 10)! instead of \lstinline!{ nk => nk % 10 }!, \begin{lstlisting} def digitsOf(n: Int): Seq[Int] = @@ -2507,7 +2404,7 @@ \section{Converting a single value into a sequence\label{sec:ch2Converting-a-sin the next element from the previous one. It is a general way of creating sequences whose length is not determined in advance. -\section{Transforming a sequence into another sequence} +\section{Transforming a sequence into another sequence\label{sec:Transforming-a-sequence}} We have seen methods such as \lstinline!map! and \lstinline!zip! that transform sequences into sequences. However, these methods cannot @@ -2550,9 +2447,6 @@ \section{Transforming a sequence into another sequence} contains the previous element of the second sequence together with a growing fragment of that sequence, which is updated as we iterate over the first sequence. The code is: - -\begin{wrapfigure}{l}{0.68\columnwidth}% -\vspace{-0.95\baselineskip} \begin{lstlisting}[numbers=left] def scanLeft[A, B](xs: Seq[A])(b0: B)(next: (B, A) => B): Seq[B] = { val init: (B, Seq[B]) = (b0, Seq(b0)) @@ -2564,11 +2458,7 @@ \section{Transforming a sequence into another sequence} result } \end{lstlisting} - -\vspace{-1.2\baselineskip} -\end{wrapfigure}% - -\noindent To implement the (nameless) updater function for \lstinline!foldLeft! +To implement the (nameless) updater function for \lstinline!foldLeft! in lines 4\textendash 6, we used a Scala feature that makes it easier to define functions with several arguments containing tuples. In our case, the updater function in \lstinline!foldLeft! has two arguments: @@ -2596,9 +2486,8 @@ \section{Summary} \item transform existing sequences into new sequences. \end{itemize} \end{itemize} -\begin{wraptable}{l}{0.59\columnwidth}% +\begin{table} \begin{centering} -\vspace{-0.5\baselineskip} \begin{tabular}{|c|c|} \hline \textbf{\small{}Definition by induction} & \textbf{\small{}Scala code example}\tabularnewline @@ -2615,11 +2504,10 @@ \section{Summary} \end{tabular} \par\end{centering} \caption{Implementing mathematical induction.\label{tab:Implementing-mathematical-induction}} -\vspace{-0.75\baselineskip} -\end{wraptable}% +\end{table} -\noindent Table~\ref{tab:Implementing-mathematical-induction} shows -Scala code implementing those tasks. Iterative calculations are implemented +Table~\ref{tab:Implementing-mathematical-induction} shows Scala +code implementing those tasks. Iterative calculations are implemented by translating mathematical induction directly into code. In the functional programming paradigm, the programmer does not need to write loops or use array indices. Instead, the programmer reasons about sequences @@ -2645,10 +2533,10 @@ \section{Summary} code via the accumulator trick, but the recursion depth is so large that stack overflows occur? There exist special techniques (e.g., \textsf{``}continuations\textsf{''}\index{continuation-passing} and \textsf{``}trampolines\textsf{''}\index{trampolines}) -that convert non-tail-recursive code into iterative code without stack -overflows. Those techniques are beyond the scope of this book. +that convert non-tail-recursive code into code that runs without stack +overflows. Those techniques are beyond the scope of this chapter. -\subsection{Solved examples\index{solved examples}} +\subsection{Examples\index{examples (with code)}} \subsubsection{Example \label{subsec:ch2Example-seq-1}\ref{subsec:ch2Example-seq-1}} @@ -2827,8 +2715,8 @@ \subsubsection{Example \label{subsec:ch2Example-binary-search-seq-4}\ref{subsec: res0: Int = 2 \end{lstlisting} -\textbf{(b)} Re-implement \lstinline!binSearch! using \lstinline!Stream.iterate! -without writing explicitly recursive code. +\textbf{(b)} Implement \lstinline!binSearch! using \lstinline!Stream.iterate! +without explicit recursion. \subparagraph{Solution} @@ -2860,7 +2748,7 @@ \subsubsection{Example \label{subsec:ch2Example-binary-search-seq-4}\ref{subsec: value of the accumulator is the pair $\left(0,N\right)$, where $N$ is the length of the entire sequence. The search is finished when $i+1=j$. For convenience, we introduce \emph{two} accumulator values -(\lstinline!left! and \lstinline!right!) representing $i$ and $j$: +(\lstinline!left! and \lstinline!right!) for $i$ and $j$: \begin{lstlisting} @tailrec def binSearch(xs: Seq[Int], goal: Int)(left: Int = 0, right: Int = xs.length): Int = { // Check whether `goal` is at one of the boundaries. @@ -2914,8 +2802,8 @@ \subsubsection{Example \label{subsec:ch2Example-binary-search-seq-4}\ref{subsec: } Stream.iterate(init)(updater) - .find { case (x, y) => y - x <= 1 } // Find the element with tight enough bounds. - .get._1 // Take the `left` bound from that element. + .find { case (x, y) => y - x <= 1 } // Find an element with tight bounds. + .get._1 // Take the `left` bound from that. } \end{lstlisting} In this code, recursion is delegated to \lstinline!Stream.iterate! @@ -2966,11 +2854,11 @@ \subsubsection{Example \label{subsec:ch2sumdigitsExample-seq-5}\ref{subsec:ch2su \end{lstlisting} This looks right; it remains to remove the first parts of the tuples: \begin{lstlisting} -def sdSeq(n: Int): Seq[Int] = Stream.iterate(n)(SD) // Stream[Int] - .scanLeft((0,0)) { case ((prev, x), next) => (x, next) } // Stream[(Int, Int)] - .drop(1).takeWhile { case (x, y) => x != y } // Stream[(Int, Int)] - .map(_._2) // Stream[Int] - .toList // List[Int] +def sdSeq(n: Int): Seq[Int] = Stream.iterate(n)(SD) // Stream[Int] + .scanLeft((0,0)) { case ((prev, x), next) => (x, next) } // Transform to Stream[(Int, Int)]. + .drop(1).takeWhile { case (x, y) => x != y } // Still Stream[(Int, Int)]. + .map(_._2) // Stream[Int] + .toList // List[Int] scala> sdSeq(99) res3: Seq[Int] = List(99, 18, 9) @@ -3000,17 +2888,17 @@ \subsubsection{Example \label{subsec:ch2Example-stream}\ref{subsec:ch2Example-st \subparagraph{Solution} We can formulate the task as an inductive definition of a stream. -If \lstinline!next(init) == None!, the stream must stop at \lstinline!init!. -(This is the base case of the induction). Otherwise, \lstinline!next(init) == Some(x)! -yields a new value \lstinline!x! and indicates that we need to continue -to \textsf{``}unfold\textsf{''} the stream with \lstinline!x! instead of \lstinline!init!. -(This is the inductive step.) Streams can be created from individual -values via the Scala standard library method \lstinline!Stream.cons! -that constructs a stream from a single value and a tail: +If \lstinline!next(init) == None!, the stream will have just one +value (\lstinline!init!). This is the base case of the induction. +Otherwise, \lstinline!next(init) == Some(x)! yields a new value \lstinline!x!. +So, we need to continue to \textsf{``}unfold\textsf{''} the stream with \lstinline!x! +instead of \lstinline!init!. (This is the inductive step.) To create +streams with given values, we use the Scala library method \lstinline!Stream.cons!. +It constructs a stream from a head value and a tail stream: \begin{lstlisting} def unfold[A](init: A)(next: A => Option[A]): Stream[A] = next(init) match { - case None => Stream(init) // A stream containing a single value `init`. - case Some(x) => Stream.cons(init, unfold(x)(next)) // `init` followed by the tail of stream. + case None => Stream(init) // A stream having a single value `init`. + case Some(x) => Stream.cons(init, unfold(x)(next)) // `init` and then the tail of the stream. } \end{lstlisting} @@ -3225,17 +3113,17 @@ \subsubsection{Example \label{subsec:ch2Example-seq-10-1}\ref{subsec:ch2Example- def longestNoDups[A](xs: Seq[A]): Seq[A] = { val init: (Seq[A], Seq[A]) = (Seq(), Seq()) val (first, last) = xs.foldLeft(init) { case ((first, current), x) => - // If `current` is empty, `x` is not considered to be repeated. + // If `current` is empty, `x` is not considered to be repeated. val xWasRepeated = current != Seq() && current.last == x val firstIsLongerThanCurrent = first.length > current.length - // Compute the new pair `(first, current)`. - // Keep `first` only if it is longer; otherwise replace it by `current`. + // Compute the new pair `(first, current)`. + // Keep `first` only if it is longer; otherwise replace it by `current`. val newFirst = if (firstIsLongerThanCurrent) first else current - // Append `x` to `current` if `x` is not repeated. + // Append `x` to `current` if `x` is not repeated. val newCurrent = if (xWasRepeated) Seq(x) else current :+ x (newFirst, newCurrent) } - // Return the longer of the two subsequences; prefer `first`. + // Return the longer of the two subsequences; prefer `first`. if (first.length >= last.length) first else last } \end{lstlisting} @@ -3245,9 +3133,10 @@ \subsection{Exercises\index{exercises}} \subsubsection{Exercise \label{subsec:ch2Exercise-seq-1}\ref{subsec:ch2Exercise-seq-1}} -Compute the sum of squared digits of a given integer; e.g., \lstinline!dsq(123) = 14! +Define a function \lstinline!dsq! that computes the sum of squared +digits of a given integer; for example, \lstinline!dsq(123) = 14! (see Example~\ref{subsec:ch2sumdigitsExample-seq-5}). Generalize -the solution to take as an argument an function \lstinline!f: Int => Int! +\lstinline!dsq! to take as an argument a function \lstinline!f: Int => Int! replacing the squaring operation. The required type signature and a sample test: \begin{lstlisting} @@ -3318,7 +3207,7 @@ \subsubsection{Exercise \label{subsec:ch2Exercise-seq-4}\ref{subsec:ch2Exercise- Hint: use \lstinline!flatMap!. -\subsubsection{Exercise \label{subsec:ch2Exercise-seq-5}\ref{subsec:ch2Exercise-seq-5}{*}} +\subsubsection{Exercise \label{subsec:ch2Exercise-seq-5}\ref{subsec:ch2Exercise-seq-5}} Same task as in Exercise~\ref{subsec:ch2Exercise-seq-4} for a set of sets: instead of just three sets \lstinline!a!, \lstinline!b!, @@ -3333,7 +3222,7 @@ \subsubsection{Exercise \label{subsec:ch2Exercise-seq-5}\ref{subsec:ch2Exercise- Hint: use \lstinline!foldLeft! and \lstinline!flatMap!. -\subsubsection{Exercise \label{subsec:ch2Exercise-seq-4-1}\ref{subsec:ch2Exercise-seq-4-1}{*}} +\subsubsection{Exercise \label{subsec:ch2Exercise-seq-4-1}\ref{subsec:ch2Exercise-seq-4-1}} In a sorted array \lstinline!xs:Array[Int]! where no values are repeated, find all pairs of values whose sum equals a given number $n$. Use @@ -3398,14 +3287,14 @@ \subsubsection{Exercise \label{subsec:ch2Exercise-seq-8}\ref{subsec:ch2Exercise- \subsubsection{Exercise \label{subsec:ch2Exercise-seq-8-1-1}\ref{subsec:ch2Exercise-seq-8-1-1}} Transform a given sequence \lstinline!xs: Seq[Int]! into a sequence -\lstinline!Seq[(Int, Int)]! of pairs that skip one neighbor. Implement -this transformation as a function \lstinline!skip1! with a type parameter -\lstinline!A! instead of the type \lstinline!Int!. The required -type signature and a sample test: +of type \lstinline!Seq[(Int, Int)]! of pairs that skip one neighbor. +Implement this transformation as a function \lstinline!skip1! with +a type parameter \lstinline!A! instead of the type \lstinline!Int!. +The required type signature and a sample test: \begin{lstlisting} def skip1[A](xs: Seq[A]): Seq[(A, A)] = ??? -scala> skip1(List(1,2,3,4,5)) +scala> skip1(List(1, 2, 3, 4, 5)) res0: List[Int] = List((1,3), (2,4), (3,5)) \end{lstlisting} @@ -3429,10 +3318,9 @@ \subsubsection{Exercise \label{subsec:ch2Exercise-seq-8-1}\ref{subsec:ch2Exercis \subsubsection{Exercise \label{subsec:ch2Exercise-seq-9}\ref{subsec:ch2Exercise-seq-9}} -Remove adjacent repeated elements from a sequence of type \lstinline!Seq[A]! -when they are repeated more than $k$ times. Repetitions up to $k$ -times should remain unchanged. The required type signature and a sample -test: +Transform a sequence by removing adjacent repeated elements when they +are repeated more than $k$ times. Repetitions up to $k$ times should +remain unchanged. The required type signature and a sample test: \begin{lstlisting} def removeDups[A](s: Seq[A], k: Int): Seq[A] = ??? @@ -3462,10 +3350,10 @@ \subsubsection{Exercise \label{subsec:ch2Exercise-seq-9-1}\ref{subsec:ch2Exercis \end{lstlisting} -\subsubsection{Exercise \label{subsec:ch2Exercise-seq-11}\ref{subsec:ch2Exercise-seq-11}{*}} +\subsubsection{Exercise \label{subsec:ch2Exercise-seq-11}\ref{subsec:ch2Exercise-seq-11}} \textbf{(a)} Remove repeated elements (whether adjacent or not) from -a sequence of type \lstinline!Seq[A]!. (This re-implements the standard +a sequence of type \lstinline!Seq[A]!. (This reproduces the standard library\textsf{'}s method \lstinline!distinct!.) \textbf{(b)} For a sequence of type \lstinline!Seq[A]!, remove all @@ -3479,7 +3367,7 @@ \subsubsection{Exercise \label{subsec:ch2Exercise-seq-11}\ref{subsec:ch2Exercise \end{lstlisting} -\subsubsection{Exercise \label{subsec:ch2Exercise-seq-10}\ref{subsec:ch2Exercise-seq-10}{*}} +\subsubsection{Exercise \label{subsec:ch2Exercise-seq-10}\ref{subsec:ch2Exercise-seq-10}} For a given sequence \lstinline!xs:Seq[Double]!, find a subsequence that has the largest sum of values. The sequence \lstinline!xs! is @@ -3494,7 +3382,7 @@ \subsubsection{Exercise \label{subsec:ch2Exercise-seq-10}\ref{subsec:ch2Exercise Hint: use \index{dynamic programming}dynamic programming and \lstinline!foldLeft!. -\subsubsection{Exercise \label{subsec:ch2Exercise-seq-12}\ref{subsec:ch2Exercise-seq-12}{*}} +\subsubsection{Exercise \label{subsec:ch2Exercise-seq-12}\ref{subsec:ch2Exercise-seq-12}} Using tail recursion, find all common integers between two \emph{sorted} sequences: @@ -3650,7 +3538,7 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v an array\textsf{'}s elements are all computed in advance and are immediately available. -Generally, there are four possible ways a value could be available: +Generally, there are three possible ways a value could be available: \begin{center} \begin{tabular}{|c|c|c|} \hline @@ -3659,11 +3547,9 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v \hline {\small{}\textsf{``}eager\index{eager value}\textsf{''}} & {\small{}computed immediately} & {\small{}}\lstinline!val z = f(123)!\tabularnewline \hline -{\small{}\textsf{``}lazy\textsf{''}} & {\small{}computed upon first use} & {\small{}}\lstinline!lazy val z = f(123)!\tabularnewline -\hline -{\small{}\textsf{``}on-call\textsf{''}} & {\small{}computed each time it is used} & {\small{}}\lstinline!def z = f(123)!\tabularnewline +{\small{}\textsf{``}lazy\textsf{''}} & {\small{}computed upon first use and stored} & {\small{}}\lstinline!lazy val z = f(123)!\tabularnewline \hline -{\small{}\textsf{``}never\textsf{''}} & {\small{}cannot be computed due to errors} & {\small{}}\lstinline!val (x, y) = "abc" !\tabularnewline +{\small{}\textsf{``}on-call\textsf{''}} & {\small{}computed each time it is needed} & {\small{}}\lstinline!def z = f(123)!\tabularnewline \hline \end{tabular} \par\end{center} @@ -3673,7 +3559,8 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v Once computed, a lazy value stays in memory and will not be re-computed. An \textsf{``}on-call\textsf{''} value\index{on-call value} is re-computed every -time it is used. In Scala, a \lstinline!def! declaration does that. +time it is used. In Scala, a \lstinline!def! declaration does that +(as well as call-by-name arguments). Most collection types in Scala (such as \lstinline!List!, \lstinline!Array!, \lstinline!Set!, and \lstinline!Map!) are \textbf{eager}\index{eager collection}. @@ -3697,13 +3584,13 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v in memory. For example: \begin{lstlisting} -scala> (1L to 1000000000L).sum // Compute the sum of integers from 1 to 1 billion. +scala> (1L to 1000000000L).sum // Compute the sum from 1 to 1 billion. res0: Long = 500000000500000000 \end{lstlisting} -We do not actually need to put a billion numbers in memory if we only -want to compute their sum. Indeed, the computation just shown does -\emph{not} put all the numbers in memory. The computation will fail -if we use a list or a stream: +We do not actually need to store a billion numbers in memory if we +only want to compute their sum. Indeed, the computation just shown +does \emph{not} store all the numbers in memory. The computation will +fail if we use a list or a stream: \begin{lstlisting} scala> (1L to 1000000000L).toStream.sum java.lang.OutOfMemoryError: GC overhead limit exceeded @@ -3737,42 +3624,30 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v to compute \emph{three} \emph{million} new numbers each time, the intermediate collection created by \lstinline!flatMap! would require too much memory, and the computation would crash: - -\begin{wrapfigure}{l}{0.545\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} scala> (1 to 10).flatMap(x => 1 to 3000000).max java.lang.OutOfMemoryError: GC overhead limit exceeded \end{lstlisting} -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -\noindent Even though the range \lstinline!(1 to 10)! is an iterator, -a subsequent \lstinline!flatMap! operation creates an intermediate -collection that is too large for our computer\textsf{'}s memory. We can use -\lstinline!view! to avoid this: - -\begin{wrapfigure}{l}{0.545\columnwidth}% -\vspace{-0.75\baselineskip} +Even though the range \lstinline!(1 to 10)! is an iterator, a subsequent +\lstinline!flatMap! operation creates an intermediate collection +that is too large for our computer\textsf{'}s memory. We can use \lstinline!view! +to avoid this: \begin{lstlisting} scala> (1 to 10).view.flatMap(x => 1 to 3000000).max res0: Int = 3000000 \end{lstlisting} -\vspace{-0.75\baselineskip} -\end{wrapfigure}% -\noindent The choice between using streams and using iterators is -dictated by memory constraints. Except for that, streams and iterators -behave similarly to other sequences. We may write programs in the -map/reduce style, applying standard methods such as \lstinline!map!, -\lstinline!filter!, etc., to streams and iterators. Mathematical -reasoning about transforming a sequence is the same, whether the sequence -is eager, lazy, or on-call. +The choice between using streams and using iterators is dictated by +memory constraints. Except for that, streams and iterators behave +similarly to other sequences. We may write programs in the map/reduce +style, applying standard methods such as \lstinline!map!, \lstinline!filter!, +etc., to streams and iterators. Mathematical reasoning about transforming +a sequence is the same, whether the sequence is eager, lazy, or on-call. \paragraph{The \texttt{Iterator} class} The Scala library class \lstinline!Iterator! has methods such as -\lstinline!Iterator.iterate! and others, similarly to \lstinline!Stream!. +\lstinline!iterate! and others, similarly to \lstinline!Stream!. However, \lstinline!Iterator! does not behave as a \emph{value} in the mathematical sense\index{Scala\textsf{'}s Iterator class@Scala\textsf{'}s \texttt{Iterator} class}: \begin{lstlisting} @@ -3839,8 +3714,8 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v scala> str.toList res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9) \end{lstlisting} -Iterators produced by applying the \lstinline!view! method to collections -will have value-like behavior: +The \lstinline!view! method produces iterators that \emph{do} have +value-like behavior: \begin{lstlisting} scala> val v = (1 until 10).view v: scala.collection.SeqView[Int,IndexedSeq[Int]] = SeqView(...) @@ -3853,7 +3728,7 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v \end{lstlisting} Due to the lack of value-like behavior, programs written using \lstinline!Iterator! -may not obey the usual rules of mathematical reasoning. \index{Scala\textsf{'}s Iterator class@Scala\textsf{'}s \texttt{Iterator} class}This +do not obey the usual rules of mathematical reasoning. \index{Scala\textsf{'}s Iterator class@Scala\textsf{'}s \texttt{Iterator} class}This makes it easy to write wrong code that looks correct. To illustrate the problem, let us re-implement Example~\ref{subsec:ch2Example-seq-7} @@ -3879,8 +3754,8 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v The error in this code occurs in the expression \lstinline!halfSpeed.zip(iter)! due to the fact that \lstinline!halfSpeed! was itself defined via \lstinline!iter!. The result is that \lstinline!iter! is used twice -in this code, which leads to errors because \lstinline!iter! is mutable -and does not behave as a value. Creating an \lstinline!Iterator! +in this code, which leads to errors. Apparently, \lstinline!iter! +\emph{does not behave as a value}! Creating an \lstinline!Iterator! and using it twice in the same expression can give wrong results or even fail with an exception: \begin{lstlisting} @@ -3890,13 +3765,10 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v scala> val t = s.zip(s).toList java.util.NoSuchElementException: next on empty iterator \end{lstlisting} -It is surprising and counter-intuitive that a variable should not -be used twice in an expression. Intuitively, we expect the code \lstinline!s.zip(s)! -to work correctly even though the variable \lstinline!s! is used -twice. When we read the expression \lstinline!s.zip(s)!, we imagine -a given sequence \lstinline!s! being \textsf{``}zipped\textsf{''} with itself. So, -we reason that \lstinline!s.zip(s)! should produce a sequence of -pairs. But Scala\textsf{'}s \lstinline!Iterator! class is \textbf{mutable}\index{mutability}: +It is surprising and counter-intuitive that a variable (here, \lstinline!s!) +cannot be used twice; we expect that the code \lstinline!s.zip(s)! +would just \textsf{``}zip\textsf{''} a given sequence \lstinline!s! with itself. +But Scala\textsf{'}s \lstinline!Iterator! class is \textbf{mutable}\index{mutability}: it gets modified during its use. This breaks the value-based reasoning about code.\index{Scala\textsf{'}s Iterator class@Scala\textsf{'}s \texttt{Iterator} class} @@ -3911,26 +3783,26 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v str: Stream[Int] = Stream(1, ?) scala> str.toList -res0: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9) +res0: List[Int] = List(1, 2, 3, 4, 5, 6) scala> str.toList -res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9) +res1: List[Int] = List(1, 2, 3, 4, 5, 6) scala> str.zip(str).toList -res2: List[(Int, Int)] = List((1,1), (2,2), (3,3), (4,4), (5,5), (6,6), (7,7), (8,8), (9,9)) +res2: List[(Int, Int)] = List((1,1), (2,2), (3,3), (4,4), (5,5), (6,6)) \end{lstlisting} Instead of \lstinline!Iterator!, we can use \lstinline!Stream! and \lstinline!view! when lazy or on-call collections are required. Newer versions of Scala replace \lstinline!Stream! with \lstinline!LazyList!, -which is a lazy (and possibly infinite) stream. Libraries such as -\lstinline!scalaz! and \lstinline!fs2! also provide lazy and on-call -streams with correct value-like behavior. - -The self-modifying behavior of \lstinline!Iterator! is an example -of a \textsf{``}side effect\textsf{''}. A function has a \index{side effect}\textbf{side -effect} if the function\textsf{'}s code performs some external action in addition -to computing a result value. Examples of side effects are: modifying +which is a lazily evaluated (and possibly infinite) stream. Libraries +such as \lstinline!scalaz! and \lstinline!fs2! also provide streams +with correct value-like behavior. + +The mutable behavior of \lstinline!Iterator! is an example of a \textsf{``}side +effect\textsf{''}. A function has a \index{side effect}\textbf{side effect} +if the function\textsf{'}s code performs some external action in addition to +computing a result value. Examples of side effects are: modifying a value stored in memory; starting and stopping processes or threads; reading or writing files; printing; sending or receiving data over a network; showing images on a display; playing or recording sounds; @@ -3946,3 +3818,18 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v function will always return the same result value when applied to the same arguments. So, pure functions behave similarly to functions that are used in mathematics. + +This book focuses on pure functions and on mathematical reasoning +about them. Statements such as \emph{\textsf{``}the }\lstinline!map!\emph{ +method cannot implement }\lstinline!sum!\emph{ because it can only +apply element-wise transformations to sequences\textsf{''}} are correct only +if the code is restricted to pure functions without side effects. +Otherwise we would write code like this: +\begin{lstlisting} +def sum(xs: Seq[Int]): Int = { + var result: Int = 0 // A mutable variable! + sum.map { x => result += x } // Side effect: mutation. + result +} +\end{lstlisting} + diff --git a/sofp-src/sofp-monads.lyx b/sofp-src/sofp-monads.lyx index f9175274e..3828a72c1 100644 --- a/sofp-src/sofp-monads.lyx +++ b/sofp-src/sofp-monads.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -424,9 +439,9 @@ map \begin_layout Standard \begin_inset Wrap figure lines 0 -placement l +placement L overhang 0in -width "40col%" +width "50col%" status open \begin_layout Plain Layout @@ -462,7 +477,7 @@ status open \end_inset -\begin_inset VSpace -60baselineskip% +\begin_inset VSpace -120baselineskip% \end_inset @@ -995,21 +1010,6 @@ map A functor block with source lines and conditionals corresponds to the mathematic al notation for creating sets of values. An example of using that notation is the formula: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "22col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1047,26 +1047,6 @@ val t = for { \end_inset -\begin_inset VSpace 60baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -30baselineskip% -\end_inset - - \begin_inset Formula \[ T=\left\{ \left.x+y+z~\right|~x\in P,\,y\in Q,\,z\in R,\,f(x,y,z)=0\,\right\} \quad. @@ -1444,12 +1424,8 @@ val result = for { \begin_layout Plain Layout -// We will cut the block here, making i and j -\end_layout - -\begin_layout Plain Layout - -// available for further computations. +// We will cut the block here, making i and j available for further computation +s. \end_layout \begin_layout Plain Layout @@ -1533,7 +1509,7 @@ val res1 = for { \begin_layout Plain Layout -} yield (i, j) // Intermediate sequence `res1`. +} yield (i, j) // Intermediate result `res1`. \end_layout \begin_layout Plain Layout @@ -1966,7 +1942,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -4353,22 +4329,7 @@ Implication is transformed as \end_inset . - These transformations (expressed in Scala at left) -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - + This is expressed in Scala as: \begin_inset listings inline false status open @@ -4391,21 +4352,12 @@ status open \end_inset -\begin_inset VSpace -80baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard \noindent -allow us to rewrite any Boolean formula as a conjunction of disjunctions - with no more nested conjunctions inside, e.g., +These transformations allow us to rewrite any Boolean formula as a conjunction + of disjunctions with no more nested conjunctions inside, e.g., \begin_inset listings inline true status open @@ -4777,21 +4729,6 @@ Set() \end_inset . -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -4814,15 +4751,6 @@ def falseCNF[A] = CNF[A](Set(Set())) \end_inset -\begin_inset VSpace -80baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard @@ -4986,21 +4914,6 @@ Set(Set()) \end_inset , a disjunction containing an empty conjunction. -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -5022,21 +4935,7 @@ def falseDNF[A] = DNF[A](Set()) \end_inset - -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -It is easy to make mistakes when reasoning about the constants +It is easy to make a mistake when setting the constants \begin_inset listings inline true status open @@ -6188,18 +6087,6 @@ matrix transposition \end_layout \begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "64col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset Box Frameless position "t" hor_pos "c" @@ -6316,19 +6203,6 @@ val matrix_T: Seq[Seq[Int]] = \end_inset -\end_layout - -\end_inset - - -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -95baselineskip% -\end_inset - - \end_layout \end_inset @@ -6337,7 +6211,6 @@ val matrix_T: Seq[Seq[Int]] = \end_layout \begin_layout Standard -\noindent To compute the transposed matrix, we have to iterate over the initial matrix. A functor block of type \begin_inset listings @@ -7245,7 +7118,7 @@ val result: Try[A] = for { // Computations in the `Try` monad. \begin_layout Plain Layout - r <- Try(h(x, y, z)) // May fail here as well. + r <- Try(h(x, y, z)) // And here. \end_layout \begin_layout Plain Layout @@ -7611,8 +7484,8 @@ val initial: Result = Right(20) // Start with a given `initial` value \begin_layout Plain Layout -scala> val result: Result = for { // Safe computation: `sqrt(1000 / - initial - 100) + 20`. +scala> val result: Result = for { // Compute `sqrt(1000 / initial - + 100) + 10`. \end_layout \begin_layout Plain Layout @@ -7632,7 +7505,7 @@ scala> val result: Result = for { // Safe computation: `sqrt(1000 / \begin_layout Plain Layout -} yield z + 1 +} yield z + 10 \end_layout \begin_layout Plain Layout @@ -7734,7 +7607,7 @@ Option status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -9377,7 +9250,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -12280,24 +12153,15 @@ final case class Writer[A, W: Semigroup](a: A, log: W) { \end_inset - -\begin_inset Note Comment -status open - -\begin_layout Plain Layout +In the code notation: \begin_inset Formula \[ -\text{flm}_{\text{Write}}(f^{:A\rightarrow B\times W})\triangleq a\times w\rightarrow\left(\pi_{1}f(a)\right)\times\left(w\oplus\pi_{2}f(a)\right)\quad. +\text{flm}_{\text{Write}}(f^{:A\rightarrow B\times W})\triangleq a\times w\rightarrow\left(f(a)\triangleright\pi_{1}\right)\times\left(w\oplus f(a)\triangleright\pi_{2}\right)\quad. \] \end_inset -\end_layout - -\end_inset - - \end_layout \begin_layout Standard @@ -12863,7 +12727,7 @@ State \end_inset - monad must be defined by the type constructor + monad is defined by the type constructor \begin_inset Formula $\text{State}^{S,A}\triangleq S\rightarrow A\times S$ \end_inset @@ -12876,7 +12740,7 @@ State \begin_inset Quotes eld \end_inset -state value +internal state value \begin_inset Quotes erd \end_inset @@ -12888,7 +12752,7 @@ state value \end_layout \begin_layout Standard -To code of the +The code of the \begin_inset listings inline true status open @@ -12915,7 +12779,7 @@ noprefix "false" \end_inset (c). - It does indeed pass the updated state value to the next computation: + The code passes the updated state value to the next computation: \begin_inset listings inline false status open @@ -12966,7 +12830,7 @@ State \end_inset - monad is the task of implementing a random number generator. + monad is when implementing a random number generator. A simple generator is the \series bold Lehmer's algorithm @@ -13032,7 +12896,7 @@ status open \begin_layout Plain Layout -def lehmer(x: Long): Long = x * 48271L % ((1L << 31) - 1) +def lehmer(x: Long): Long = (x * 48271L) % ((1L << 31) - 1L) \end_layout \end_inset @@ -13050,8 +12914,8 @@ status open \begin_layout Plain Layout -def uniform(x: Long): Double = (x - 1).toDouble / ((1L << 31) - 3) // Enforce - the interval [0, 1]. +def uniform(x: Long): Double = (x - 1L).toDouble / ((1L << 31) - 3L) // + Enforce the interval [0, 1]. \end_layout \end_inset @@ -13086,21 +12950,6 @@ lehmer repeatedly on successive values. The code would look like this: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -13157,23 +13006,6 @@ val s3 = lehmer(s2) // And so on. \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent We need to keep track of the generator's state values \begin_inset listings inline true @@ -13269,7 +13101,7 @@ val rngUniform: State[Long, Double] = { oldState => \begin_layout Plain Layout - val result = uniform(oldState) // Enforce the interval [0, 1]. + val result = uniform(oldState) \end_layout \begin_layout Plain Layout @@ -13731,21 +13563,6 @@ Eval \end_inset , we may use functor blocks to combine eager and lazy computations freely: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "63.1col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -13777,23 +13594,6 @@ val result: Eval[Int] = for { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The value of \begin_inset listings inline true @@ -13993,21 +13793,6 @@ pure \end_inset with a callback argument: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -14068,26 +13853,43 @@ def result(callback: Int => Unit): Unit = \end_inset +This code illustrates the continuation-passing style: each step of the calculati +on calls a function (a +\begin_inset Quotes eld +\end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% +continuation +\begin_inset Quotes erd \end_inset +) passed to it as the last curried argument. + The final result of the calculation is not returned by the function +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +\noindent + +result \end_layout \end_inset + but is available only as the bound variable +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +\noindent +z \end_layout -\begin_layout Standard -\noindent -This code is a typical pattern of continuation-passing style. - The final result of the calculation is only available as the bound variable - +\end_inset + + in a deeply nested function scope where \begin_inset listings inline true status open @@ -14095,15 +13897,14 @@ status open \begin_layout Plain Layout \noindent -z +callback(z) \end_layout \end_inset - in a deeply nested function scope. - This makes working with this code style more difficult: We can continue - the program only by writing code either directly within that deeply nested - scope or within the given + is run. + If we need to extend this program with some more calculation steps, we + would have to add extra code within the deepest-nested scope of \begin_inset listings inline true status open @@ -14111,21 +13912,25 @@ status open \begin_layout Plain Layout \noindent -callback +result \end_layout \end_inset -. - This is the heuristic reason why callback programming is known as -\begin_inset Quotes eld -\end_inset +, or we could pass a +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +\noindent + +callback +\end_layout -continuation-passing -\begin_inset Quotes erd \end_inset - style. + argument that contains further steps. \end_layout \begin_layout Standard @@ -14283,7 +14088,7 @@ registers \begin_layout Standard One complication is that it is difficult to maintain code that contains deeply nested function scopes. - The continuation monad solves this problem by converting nested function + The continuation monad solves that problem by converting nested function scopes into more easily composable functor blocks. The code also becomes more readable. \end_layout @@ -14294,13 +14099,12 @@ To derive the required type constructor, consider a computation of type \begin_inset Formula $A\rightarrow B$ \end_inset - that needs to use the continuation-passing style. + that needs to be converted to the continuation-passing style. Instead of returning a value of type \begin_inset Formula $B$ \end_inset -, it registers a callback function for possible later use and returns a - +, it registers a callback function and returns a \begin_inset listings inline true status open @@ -14347,6 +14151,10 @@ Unit \end_inset + +\end_layout + +\begin_layout Standard It is sometimes helpful if the callback returns a more informative value than \begin_inset listings @@ -14363,11 +14171,8 @@ Unit . For instance, that value could show error information or give access to processes that were scheduled concurrently. -\end_layout - -\begin_layout Standard -So, we generalize the type constructor -\begin_inset Formula $L$ + So, we generalize the type constructor +\begin_inset Formula $L^{A}$ \end_inset to @@ -14471,7 +14276,7 @@ flatMap \end_inset - substitutes a new callback, + substitutes a new callback ( \begin_inset listings inline true status open @@ -14483,7 +14288,7 @@ br: B => R \end_inset -, into the innermost scope of the computation +) into the innermost scope of the computation \begin_inset listings inline true status open @@ -14496,7 +14301,7 @@ f \end_inset . - In this way, we obtain easy access to the innermost callback scope. + In this way, we obtain direct access to the innermost callback scope. This trick makes code with deeply nested callbacks composable. \end_layout @@ -14657,21 +14462,6 @@ Future \end_inset value that resolves when the callback is called: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "51.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -14713,23 +14503,6 @@ Await.result(runner(result), Duration.Inf) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent This runner uses special low-level features of the \begin_inset listings inline true @@ -14811,7 +14584,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -14820,7 +14593,7 @@ solved examples \end_layout \begin_layout Standard -Each arithmetic computation, such as +Suppose that each arithmetic computation, such as \begin_inset listings inline true status open @@ -14844,7 +14617,7 @@ mult4 \end_inset -, now has a certain cost, which is a value of a monoid type +, has a certain cost, which is a value of a monoid type \begin_inset Formula $W$ \end_inset @@ -14861,7 +14634,7 @@ Cont[W, A] \end_inset - to implement computations with a specified cost. + to implement computations with a cost. The total cost must add up automatically when computations are chained using \begin_inset listings @@ -15068,7 +14841,7 @@ val resultInt = Await.result(futureResult, Duration.Inf) // Wait for the \end_layout \begin_layout Standard -If the code contains many other monadic operations such as +If the code contains many monadic operations similar to \begin_inset listings inline true status open @@ -17835,7 +17608,7 @@ Laws of semi-monads and monads \end_layout \begin_layout Subsection -Motivation for the semi-monad laws +Motivating the semi-monad laws \end_layout \begin_layout Standard @@ -18036,7 +17809,7 @@ val result1 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewrite via method chains: \end_layout \begin_layout Plain Layout @@ -18096,7 +17869,7 @@ val result2 = for { \begin_layout Plain Layout - z <- g(y) // This code is unchanged. + z <- g(y) // This is unchanged. \end_layout \begin_layout Plain Layout @@ -18106,7 +17879,7 @@ val result2 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewrite via method chains: \end_layout \begin_layout Plain Layout @@ -18229,7 +18002,7 @@ val result1 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewrite via method chains: \end_layout \begin_layout Plain Layout @@ -18299,7 +18072,7 @@ val result2 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewrite via method chains: \end_layout \begin_layout Plain Layout @@ -18396,7 +18169,7 @@ val result1 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewrite via method chains: \end_layout \begin_layout Plain Layout @@ -18466,7 +18239,7 @@ val result2 = for { \begin_layout Plain Layout - // Translating the functor block into methods: + // Rewrite via method chains: \end_layout \begin_layout Plain Layout @@ -18626,45 +18399,18 @@ def flm[A, B](f: A => S[B]): S[A] => S[B] \end_inset The first law is written in Scala code as: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "37col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open \begin_layout Plain Layout -c.flatMap(x => g(f(x))) == -\end_layout - -\begin_layout Plain Layout - - c.map(f).flatMap(g) +c.flatMap(x => g(f(x))) == c.map(f).flatMap(g) \end_layout \end_inset -\begin_inset VSpace 20baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{0.2pc}\xyScaleX{3pc} & S^{B}\ar[rd]\sp(0.5){~~\ \text{flm}\,(g^{:B\rightarrow S^{C}})}\\ @@ -18674,23 +18420,6 @@ S^{A}\ar[ru]\sp(0.5){f^{\uparrow S}}\ar[rr]\sb(0.5){\text{flm}\,(f^{:A\rightarro \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In the code notation, we write this law in the point-free style \begin_inset Index idx status open @@ -18857,45 +18586,18 @@ The second law holds for arbitrary \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open \begin_layout Plain Layout -c.flatMap(f).map(g) == -\end_layout - -\begin_layout Plain Layout - - c.flatMap { x => f(x).map(g) } +c.flatMap(f).map(g) == c.flatMap { x => f(x).map(g) } \end_layout \end_inset -\begin_inset VSpace 20baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{0.2pc}\xyScaleX{3pc} & S^{B}\ar[rd]\sp(0.5){\ \,(g^{:B\rightarrow C})^{\uparrow S}}\\ @@ -18906,26 +18608,9 @@ S^{A}\ar[ru]\sp(0.5){\text{flm}\,(f^{:A\rightarrow S^{B}})~~~}\ar[rr]\sb(0.5){\t \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -20baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\noindent -\begin_inset VSpace -35baselineskip% -\end_inset - - \begin_inset Formula \begin{equation} \text{flm}\,(f^{:A\rightarrow S^{B}}\bef g^{\uparrow S})=\text{flm}\,(f)\bef g^{\uparrow S}\quad.\label{eq:right-naturality-law-flatMap} @@ -19035,42 +18720,15 @@ flatMap \end_inset operations: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open \begin_layout Plain Layout -c.flatMap { x => f(x).flatMap(g) } == +c.flatMap { x => f(x).flatMap(g) } == c.flatMap(f).flatMap(g) \end_layout -\begin_layout Plain Layout - - c.flatMap(f).flatMap(g) -\end_layout - -\end_inset - - -\begin_inset VSpace 0baselineskip% \end_inset @@ -19084,29 +18742,6 @@ S^{A}\ar[ru]\sp(0.5){\text{flm}\,(f^{:A\rightarrow S^{B}})~~\ }\ar[rr]\sb(0.5){\ \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace 60baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -160baselineskip% -\end_inset - - -\end_layout - -\begin_layout Standard \begin_inset Formula \begin{equation} \text{flm}\,\big(f^{:A\rightarrow S^{B}}\bef\text{flm}\,(g^{:B\rightarrow S^{C}})\big)=\text{flm}\left(f\right)\bef\text{flm}\left(g\right)\quad.\label{eq:associativity-law-flatMap} @@ -19114,17 +18749,6 @@ S^{A}\ar[ru]\sp(0.5){\text{flm}\,(f^{:A\rightarrow S^{B}})~~\ }\ar[rr]\sb(0.5){\ \end_inset - -\end_layout - -\begin_layout Standard -\begin_inset VSpace -20baselineskip% -\end_inset - - -\end_layout - -\begin_layout Standard This equation is called the \series bold associativity law @@ -19464,24 +19088,6 @@ flatten \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -19494,10 +19100,6 @@ c.flatMap(f) == c.map(f).flatten \end_inset -\begin_inset VSpace 20baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{0.3pc}\xyScaleX{3.5pc} & S^{S^{B}}\ar[rd]\sp(0.5){\ \text{ftn}\ }\\ @@ -19508,29 +19110,6 @@ S^{A}\ar[ru]\sp(0.5){(f^{:A\rightarrow S^{B}})^{\uparrow S}\ }\ar[rr]\sp(0.5){\t \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace 130baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset Formula \[ \text{flm}_{S}(f)=f^{\uparrow S}\bef\text{ftn}_{S}\quad. @@ -20523,7 +20102,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -20590,21 +20169,6 @@ flatten \end_inset , and its code is: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "60col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -20baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -20637,29 +20201,6 @@ def flatten[A]: Either[Z, Either[Z, A]] => Either[Z, A] = { \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -320baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -120baselineskip% -\end_inset - - \begin_inset Formula \[ \text{ftn}^{:Z+Z+A\rightarrow Z+A}\triangleq\,\begin{array}{|c||cc|} @@ -20673,10 +20214,6 @@ A & \bbnum 0 & \text{id} \end_inset -\begin_inset VSpace -80baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -20692,8 +20229,8 @@ flatten \end_inset - is fully parametric, both sides of the law are fully parametric functions - with the type signature + is fully parametric, both sides of the associativity law are fully parametric + functions with the type signature \begin_inset Formula $Z+Z+Z+A\rightarrow Z+A$ \end_inset @@ -21786,7 +21323,7 @@ Had the code not exchanged the order of \end_layout \begin_layout Subsection -From semi-monads to monads: Motivation for the identity laws +From semi-monads to monads: Motivating the identity laws \end_layout \begin_layout Standard @@ -21870,7 +21407,7 @@ width "47.5col%" status open \begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% +\begin_inset VSpace -40baselineskip% \end_inset @@ -21902,7 +21439,7 @@ def pure[A](a: A): M[A] \end_inset -\begin_inset VSpace -50baselineskip% +\begin_inset VSpace -40baselineskip% \end_inset @@ -21973,21 +21510,9 @@ y = x \end_inset . - This line must occur either before or after another source line, for instance: -\end_layout - -\begin_layout Standard -\begin_inset Note Comment -status open - -\begin_layout Plain Layout -So, we need to examine two possibilities: first, an empty effect comes before - another source line, -\end_layout - -\end_inset - - + This line must occur either before or after another source line. + So, we need to examine two situations: first, when an empty effect comes + before another source line: \end_layout \begin_layout Standard @@ -22036,25 +21561,17 @@ result1 = for { \begin_layout Plain Layout - y <- pure(x) // -\begin_inset Quotes eld -\end_inset - -Empty effect -\begin_inset Quotes erd -\end_inset - - with x: A. + y <- pure(x) // Empty effect, x:A \end_layout \begin_layout Plain Layout - z <- someArray(y) // someArray: A => M[B] + z <- p(y) // p: A => M[B] \end_layout \begin_layout Plain Layout - // Same as z <- pure(x).flatMap(someArray) + // Same as z <- pure(x).flatMap(p) \end_layout \end_inset @@ -22109,17 +21626,17 @@ result2 = for { \begin_layout Plain Layout - y = x // x: A + y = x // x: A \end_layout \begin_layout Plain Layout - z <- someArray(y) // someArray: A => M[B] + z <- p(y) // p: A => M[B] \end_layout \begin_layout Plain Layout - // Same as z <- someArray(x) + // Same as z <- p(x) \end_layout \end_inset @@ -22166,21 +21683,6 @@ result2 \end_inset , -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "30col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -22193,26 +21695,6 @@ pure(x).flatMap(g) == g(x) \end_inset -\begin_inset VSpace -60baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -140baselineskip% -\end_inset - - \begin_inset Formula \begin{align} \text{left identity law of }M:\quad & \text{pu}_{M}\bef\text{flm}_{M}(g^{:A\rightarrow M^{B}})=g\quad.\label{eq:monad-left-identity-law-for-flatMap} @@ -22221,14 +21703,10 @@ pure(x).flatMap(g) == g(x) \end_inset -\begin_inset VSpace -120baselineskip% -\end_inset - - \end_layout \begin_layout Standard -The second possibility is that an empty effect comes +The second situation is when an empty effect comes \emph on after \emph default @@ -22275,17 +21753,17 @@ result1 = for { \begin_layout Plain Layout - x <- someArray // someArray: M[A] + x <- p // p: M[A] \end_layout \begin_layout Plain Layout - y <- pure(x) // Empty effect with x: A. + y <- pure(x) // Empty effect. \end_layout \begin_layout Plain Layout -// Same as y <- someArray.flatMap(x => pure(x)) +// Same as y <- p.flatMap(x => pure(x)) \end_layout \end_inset @@ -22334,7 +21812,7 @@ result2 = for { \begin_layout Plain Layout - x <- someArray // someArray: M[A] + x <- someArray // p: M[A] \end_layout \begin_layout Plain Layout @@ -22344,7 +21822,7 @@ result2 = for { \begin_layout Plain Layout - // Same as y <- someArray + // Same as y <- p \end_layout \end_inset @@ -22387,21 +21865,6 @@ result2 \end_inset gives the law: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "30col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -22414,26 +21877,6 @@ g.flatMap(pure) == g \end_inset -\begin_inset VSpace -60baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -140baselineskip% -\end_inset - - \begin_inset Formula \begin{align} \text{right identity law of }M:\quad & \text{flm}_{M}(\text{pu}_{M})=\text{id}^{:M^{A}\rightarrow M^{A}}\quad.\label{eq:monad-right-identity-law-for-flatMap} @@ -22821,21 +22264,6 @@ flatMap \end_inset , written as: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "32col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -0baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{1.0pc}\xyScaleX{3pc} & M^{M^{A}}\ar[rd]\sp(0.5){\ \text{ftn}^{A}}\\ @@ -22846,26 +22274,6 @@ M^{A}\ar[ru]\sp(0.5){\text{pu}^{M^{A}}}\ar[rr]\sb(0.5){\text{id}} & & M^{A} \end_inset -\begin_inset VSpace -20baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset Formula \[ \text{pu}_{M}\bef\text{flm}_{M}(f)=f\quad. @@ -23000,21 +22408,6 @@ flatten \end_inset is written as: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "32col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -160baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{0.2pc}\xyScaleX{3pc} & M^{M^{A}}\ar[rd]\sp(0.5){\ \text{ftn}^{A}\ }\\ @@ -23025,26 +22418,6 @@ M^{A}\ar[ru]\sp(0.5){(\text{pu}^{A})^{\uparrow M}\quad}\ar[rr]\sb(0.5){\text{id} \end_inset -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset Formula \begin{align} & \text{flm}_{M}(\text{pu}_{M})=\text{pu}_{M}^{\uparrow M}\bef\text{ftn}_{M}\overset{!}{=}\text{id}\quad.\label{eq:right-identity-law-for-flatten} @@ -23053,14 +22426,10 @@ M^{A}\ar[ru]\sp(0.5){(\text{pu}^{A})^{\uparrow M}\quad}\ar[rr]\sb(0.5){\text{id} \end_inset -\begin_inset VSpace -10baselineskip% -\end_inset - - \end_layout \begin_layout Standard -In the next section, we will see a reason why these laws have their names. +In the next section, we will see reasons why these laws have their names. \end_layout \begin_layout Subsection @@ -23218,21 +22587,6 @@ This resembles the forward composition of ordinary functions, \end_inset is: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "67col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -23250,26 +22604,6 @@ def <>[M[_]: Monad, A,B,C](f: A => M[B], g: B => M[C]): A => M[C] = \end_inset -\begin_inset VSpace -90baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset Formula \begin{equation} f\diamond_{_{M}}g\triangleq f\bef\text{flm}_{M}(g)\quad.\label{eq:def-of-kleisli-composition-for-monad-via-flatMap} @@ -23278,15 +22612,10 @@ f\diamond_{_{M}}g\triangleq f\bef\text{flm}_{M}(g)\quad.\label{eq:def-of-kleisli \end_inset -\begin_inset VSpace -50baselineskip% -\end_inset - - \end_layout \begin_layout Standard -The Kleisli composition can be equivalently expressed by a functor block - code as: +The Kleisli composition can be equivalently expressed by a functor block: \end_layout \begin_layout Standard @@ -23574,21 +22903,6 @@ flatMap \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "32col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{0.8pc}\xyScaleX{3pc} & M^{B}\ar[rd]\sp(0.5){\ \text{flm}_{M}(g)\ }\\ @@ -23599,26 +22913,6 @@ M^{A}\ar[ru]\sp(0.5){\text{flm}_{M}(f)\quad}\ar[rr]\sb(0.5){\text{flm}_{M}(f\dia \end_inset -\begin_inset VSpace 10baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -20baselineskip% -\end_inset - - \begin_inset Formula \[ \text{flm}_{M}(f\diamond_{_{_{M}}}g)=\text{flm}_{M}(f)\bef\text{flm}_{M}(g)\quad. @@ -23627,10 +22921,6 @@ M^{A}\ar[ru]\sp(0.5){\text{flm}_{M}(f)\quad}\ar[rr]\sb(0.5){\text{flm}_{M}(f\dia \end_inset -\begin_inset VSpace -80baselineskip% -\end_inset - - \end_layout \begin_layout Subparagraph @@ -23833,24 +23123,6 @@ Since the Kleisli composition describes the chaining of consecutive lines in functor blocks, its associativity means that multiple lines are chained unambiguously. For example, this code: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "14col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace 10baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings lstparams "numbers=left" inline false @@ -23883,34 +23155,17 @@ x => for { \end_inset - -\begin_inset VSpace 0baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -corresponds to the Kleisli composition: -\begin_inset VSpace -20baselineskip% -\end_inset - - +corresponds to this Kleisli composition: \begin_inset Formula \[ -(x\rightarrow f(x))\diamond_{_{_{M}}}(y\rightarrow g(y))\diamond_{_{_{M}}}(z\rightarrow h(z)) +(x\rightarrow f(x))\diamond_{_{_{M}}}(y\rightarrow g(y))\diamond_{_{_{M}}}(z\rightarrow h(z))\quad. \] \end_inset -and does not need to specify whether lines 2 and 3 are chained before appending - line 4, or lines 3 and 4 are chained before prepending line 2. +Because of the associativity law, the programmer does not need to specify + whether lines 2 and 3 are chained before appending line 4, or lines 3 and + 4 are chained before prepending line 2. \end_layout \begin_layout Standard @@ -24559,7 +23814,7 @@ The continuation monad, \begin_inset Formula $\text{Cont}^{R,A}\triangleq\left(A\rightarrow R\right)\rightarrow R$ \end_inset -, satisfies all monad laws. +, obeys the monad laws. \end_layout \begin_layout Subparagraph @@ -24754,7 +24009,7 @@ The state monad, \begin_inset Formula $\text{State}^{S,A}\triangleq S\rightarrow A\times S$ \end_inset -, satisfies all monad laws. +, obeys the monad laws. \end_layout \begin_layout Subparagraph @@ -24918,7 +24173,11 @@ noprefix "false" \end_inset . - When a monad + +\end_layout + +\begin_layout Standard +When a monad \begin_inset Formula $M$ \end_inset @@ -24927,8 +24186,8 @@ noprefix "false" \end_inset has two curried arguments. - Flipping or uncurrying these arguments often produces an equivalent function - that is easier to work with. + Flipping or uncurrying those arguments often gives an equivalent type that + is easier to work with. \end_layout @@ -26077,7 +25336,7 @@ has_inner_box 1 inner_pos "t" use_parbox 0 use_makebox 0 -width "30col%" +width "35col%" special "none" height "1in" height_special "totalheight" @@ -26731,7 +25990,7 @@ width "55col%" status open \begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% +\begin_inset VSpace -60baselineskip% \end_inset @@ -26786,7 +26045,7 @@ def gamma[A]: L[A] => F[A] = { \end_inset -\begin_inset VSpace -20baselineskip% +\begin_inset VSpace -40baselineskip% \end_inset @@ -26838,22 +26097,7 @@ flatten \end_inset - method: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "55col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - + method \begin_inset listings inline false status open @@ -26886,29 +26130,6 @@ def flatten_L[A]: L[L[A]] => L[A] = { \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -300baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -110baselineskip% -\end_inset - - \begin_inset Formula \[ \text{ftn}_{L}\triangleq\,\begin{array}{|c||cc|} @@ -27255,7 +26476,7 @@ F^{L^{L^{A}}} & \bbnum 0 & \gamma^{\uparrow F}\bef\text{ftn}_{F}\bef\gamma^{\upa \begin_inset Note Note -status collapsed +status open \begin_layout Plain Layout \begin_inset Formula @@ -27714,13 +26935,13 @@ f^{:Z\rightarrow A\rightarrow F^{B}}\tilde{\diamond}_{_{L}}g^{:Z\rightarrow B\ri \begin_inset Note Note -status collapsed +status open \begin_layout Plain Layout \begin_inset Formula \begin{align*} - & f^{:Z\rightarrow A\rightarrow F^{B}}\tilde{\diamond}_{_{L}}g^{:Z\rightarrow B\rightarrow F^{C}}\triangleq z^{:Z}\rightarrow f(z)\diamond_{_{F}}g(z)\quad,\\ - & \tilde{\text{pu}}_{L}\triangleq\_^{:Z}\rightarrow\text{pu}_{F}\quad. +f^{:Z\rightarrow A\rightarrow F^{B}}\tilde{\diamond}_{_{L}}g^{:Z\rightarrow B\rightarrow F^{C}} & \triangleq z^{:Z}\rightarrow f(z)\diamond_{_{F}}g(z)\quad,\\ +\tilde{\text{pu}}_{L} & \triangleq\_^{:Z}\rightarrow\text{pu}_{F}\quad. \end{align*} \end_inset @@ -29433,7 +28654,7 @@ In order to compute function compositions such as , so that their output types are fully split: \begin_inset Note Note -status collapsed +status open \begin_layout Plain Layout \begin_inset Formula @@ -29552,7 +28773,7 @@ F^{L^{A}} & \bbnum 0 & \text{id} \begin_inset Note Note -status collapsed +status open \begin_layout Plain Layout \begin_inset Formula @@ -29667,7 +28888,7 @@ F^{L^{L^{L^{A}}}} & \bbnum 0 & \big(\text{ftn}_{L}^{\overline{\uparrow L}}\big)^ \begin_inset Note Note -status collapsed +status open \begin_layout Plain Layout \begin_inset Formula @@ -30953,21 +30174,6 @@ toUnit \end_inset ) with type signature: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "61col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -30980,29 +30186,6 @@ def toUnit[M[_]: Monad, A]: M[A] => M[Unit] = _.map(_ => ()) \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -90baselineskip% -\end_inset - - \begin_inset Formula \[ \text{tu}_{M}:M^{A}\rightarrow M^{\bbnum 1}\triangleq(\_^{:A}\rightarrow1)^{\uparrow M}\quad. @@ -31946,21 +31129,6 @@ flatMap \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -31973,29 +31141,6 @@ p.map(f) == p.flatMap(f andThen M.pure) \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -10baselineskip% -\end_inset - - \begin_inset Formula \begin{equation} f^{\uparrow M}=\text{flm}_{M}(f\bef\text{pu}_{M})\quad.\label{eq:express-map-through-flatMap} @@ -33660,21 +32805,6 @@ ftr \end_inset (of the above types) are in a one-to-one correspondence: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "30col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -190baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{0.5pc}\xyScaleX{2.8pc} & F^{G^{B}}\ar[rd]\sp(0.5){\ \text{tr}\ }\\ @@ -33685,26 +32815,6 @@ F^{A}\ar[ru]\sp(0.5){(f^{:A\rightarrow G^{B}})^{\uparrow F}~\ }\ar[rr]\sb(0.5){\ \end_inset -\begin_inset VSpace -60baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset Formula \begin{equation} \text{ftr}^{A,B}(f)=f^{\uparrow F}\bef\text{tr}^{B}\quad,\quad\quad\text{tr}^{A}=\text{ftr}^{A,A}(\text{id}^{:A\rightarrow A})\quad,\label{eq:define-tr-via-ftr} @@ -34250,6 +33360,14 @@ def runner[S, A](init: S): State[S, A] => A = _.run(init)._1 \end_inset + +\begin_inset Formula +\begin{equation} +\text{run}_{\text{State}}:S\rightarrow\text{State}^{S,A}\rightarrow A\quad,\quad\quad\text{run}_{\text{State}}(s_{0})\triangleq k^{:S\rightarrow A\times S}\rightarrow s_{0}\triangleright k\triangleright\pi_{1}\quad.\label{eq:definition-of-runState} +\end{equation} + +\end_inset + The runner for the \begin_inset listings inline true @@ -34355,24 +33473,6 @@ To be useful in practice, a runner \end_inset ) of the first monadic program. -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -34399,20 +33499,6 @@ val m = for { // m == m1.flatMap(m2) \end_inset - -\begin_inset VSpace -60baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The composition of \begin_inset Formula $m_{1}$ \end_inset @@ -34441,14 +33527,6 @@ m=m_{1}\triangleright\text{flm}_{M}(m_{2})\quad.\label{eq:monad-runners-derivati \end_inset - -\begin_inset VSpace -120baselineskip% -\end_inset - - -\end_layout - -\begin_layout Standard We may imagine that \begin_inset Formula $\theta\left(m\right)$ \end_inset @@ -34510,24 +33588,6 @@ composition law!of monad runners composition law \series default : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -34540,26 +33600,6 @@ runner(m) == runner(m2(runner(m1))) \end_inset -\begin_inset VSpace -20baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset Formula \[ m\triangleright\theta=m_{1}\triangleright\theta\triangleright m_{2}\triangleright\theta\quad. @@ -34679,24 +33719,9 @@ It follows that . The runner's composition law becomes: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "20col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -190baselineskip% -\end_inset - - \begin_inset Formula \[ -\xymatrix{\xyScaleY{2.0pc}\xyScaleX{2.3pc}M^{M^{A}}\ar[d]\sb(0.5){\text{ftn}_{M}}\ar[r]\sp(0.5){\theta^{\uparrow M}}\sb(0.5){\theta^{M^{A}}} & M^{A}\ar[d]\sb(0.45){\theta}\\ +\xymatrix{\xyScaleY{2.0pc}\xyScaleX{2.3pc}M^{M^{A}}\ar[d]\sb(0.5){\text{ftn}_{M}}\ar[r]\sp(0.5){\theta^{\uparrow M}} & M^{A}\ar[d]\sb(0.45){\theta}\\ M^{A}\ar[r]\sp(0.5){\theta} & A } \] @@ -34704,26 +33729,6 @@ M^{A}\ar[r]\sp(0.5){\theta} & A \end_inset -\begin_inset VSpace -0baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -40baselineskip% -\end_inset - - \begin_inset Formula \begin{equation} \text{ftn}_{M}\bef\theta=\theta\bef\theta=\theta^{\uparrow M}\bef\theta\quad.\label{eq:runner-composition-law} @@ -35085,24 +34090,6 @@ Option \end_inset monad like this: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "43col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -45baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -35136,26 +34123,6 @@ def run[A]: Option[A] => Either[E, A] = { \end_inset -\begin_inset VSpace -220baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset Formula \[ \theta^{:\bbnum 1+A\rightarrow E+A}\triangleq\begin{array}{|c||cc|} @@ -35601,10 +34568,6 @@ identity laws!of monad morphisms \end_inset -\begin_inset VSpace -120baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{1.2pc}\xyScaleX{4.8pc}A\ar[d]\sb(0.5){\text{pu}_{M}}\ar[rd]\sp(0.5){\text{pu}_{N}} & & M^{M^{A}}\ar[d]\sb(0.5){\phi^{\uparrow M}}\ar[r]\sp(0.5){\text{ftn}_{M}} & M^{A}\ar[rd]\sp(0.5){\phi}\\ @@ -35880,7 +34843,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -35923,24 +34886,6 @@ Option \end_inset is: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\begin_layout Plain Layout \begin_inset listings inline false status open @@ -35968,26 +34913,6 @@ def toOption[Z, A]: Either[Z, A] => Option[A] = { \end_inset -\begin_inset VSpace -170baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -70baselineskip% -\end_inset - - \begin_inset Formula \[ \phi\triangleq\,\begin{array}{|c||cc|} @@ -36120,7 +35045,7 @@ A & \bbnum 0 & \text{id} \begin_inset Note Note -status collapsed +status open \begin_layout Plain Layout \begin_inset Formula @@ -36190,7 +35115,7 @@ A & \bbnum 0 & \text{id} \begin_inset Note Note -status collapsed +status open \begin_layout Plain Layout \begin_inset Formula diff --git a/sofp-src/sofp-monads.tex b/sofp-src/sofp-monads.tex index 8277a328e..410c63e98 100644 --- a/sofp-src/sofp-monads.tex +++ b/sofp-src/sofp-monads.tex @@ -24,7 +24,7 @@ \subsection{Motivation for semi-monads: Nested iteration} Recall that a \textsf{``}flat\textsf{''} (non-nested) iteration is translated into the \lstinline!map! method applied to a sequence: -\begin{wrapfigure}{l}{0.4\columnwidth}% +\begin{wrapfigure}{L}{0.5\columnwidth}% \vspace{-0.45\baselineskip} \begin{lstlisting} (1 to n).map { i => 1.0 / (1 + i) }.sum @@ -32,7 +32,7 @@ \subsection{Motivation for semi-monads: Nested iteration} \vspace{-0.5\baselineskip} \end{wrapfigure}% -~\vspace{-0.6\baselineskip} +~\vspace{-1.2\baselineskip} \[ \sum_{i=1}^{n}\frac{1}{1+i}\quad. \] @@ -122,9 +122,6 @@ \subsection{Motivation for semi-monads: Nested iteration} A functor block with source lines and conditionals corresponds to the mathematical notation for creating sets of values. An example of using that notation is the formula: - -\begin{wrapfigure}{l}{0.22\columnwidth}% -\vspace{-0.6\baselineskip} \begin{lstlisting} val t = for { x <- p @@ -133,10 +130,6 @@ \subsection{Motivation for semi-monads: Nested iteration} if f(x, y, z) == 0 } yield x + y + z \end{lstlisting} -\vspace{0.6\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.3\baselineskip} \[ T=\left\{ \left.x+y+z~\right|~x\in P,\,y\in Q,\,z\in R,\,f(x,y,z)=0\,\right\} \quad. \] @@ -199,8 +192,7 @@ \subsection{Motivation for semi-monads: Nested iteration} val result = for { i <- 1 to m j <- 1 to n -// We will cut the block here, making i and j -// available for further computations. +// We will cut the block here, making i and j available for further computations. x = f(i, j) k <- 1 to p y = g(i, j, k) @@ -215,7 +207,7 @@ \subsection{Motivation for semi-monads: Nested iteration} val res1 = for { i <- 1 to m j <- 1 to n -} yield (i, j) // Intermediate sequence `res1`. +} yield (i, j) // Intermediate result `res1`. val res2 = for { (i, j) <- res1 // Continue from `res1`. x = f(i, j) @@ -272,7 +264,7 @@ \subsection{List-like monads} monads is to obtain a set of all possible solutions of a combinatorial problem. One can then filter out unwanted combinations. -\subsubsection{Example \label{subsec:Example-list-monads-1}\ref{subsec:Example-list-monads-1}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-list-monads-1}\ref{subsec:Example-list-monads-1}\index{examples (with code)}} Compute all permutations of the three letters \lstinline!"a", "b", "c"!. @@ -610,21 +602,17 @@ \subsubsection{Example \label{subsec:Example-list-monads-7}\ref{subsec:Example-l {\color{greenunder}\text{expand brackets with a disjunction inside}:}\quad & (a\vee b)\wedge c=\left(a\wedge c\right)\vee\left(b\wedge c\right)\quad. \end{align*} Implication is transformed as $(a\Rightarrow b)=((\neg a)\vee b)$. -These transformations (expressed in Scala at left) - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.6\baselineskip} +This is expressed in Scala as: \begin{lstlisting} (a && b) || c == (a || c) && (b || c) (a || b) && c == (a && c) || (b && c) (a implies b) == (!a) || b \end{lstlisting} -\vspace{-0.8\baselineskip} -\end{wrapfigure}% -\noindent allow us to rewrite any Boolean formula as a conjunction -of disjunctions with no more nested conjunctions inside, e.g., \lstinline*(a || b) && ((!c) || d || e)*. -This form is called the \textbf{conjunctive normal form}\index{conjunctive normal form}\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Conjunctive_normal_form}{https://en.wikipedia.org/wiki/Conjunctive\_normal\_form}}} +\noindent These transformations allow us to rewrite any Boolean formula +as a conjunction of disjunctions with no more nested conjunctions +inside, e.g., \lstinline*(a || b) && ((!c) || d || e)*. This form +is called the \textbf{conjunctive normal form}\index{conjunctive normal form}\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Conjunctive_normal_form}{https://en.wikipedia.org/wiki/Conjunctive\_normal\_form}}} (CNF) of a Boolean formula. We can also rewrite Boolean formula into a \emph{disjunction} of conjunctions with no more nesting inside, e.g., \lstinline*(p && q && !r) || ((!x) && y)*. This is called the @@ -660,16 +648,11 @@ \subsubsection{Example \label{subsec:Example-list-monads-7}\ref{subsec:Example-l \end{lstlisting} This must hold for any formulas; so we must represent \lstinline!true! as an \emph{empty} set of disjunctions, \lstinline!Set()!. - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.6\baselineskip} \begin{lstlisting} final case class CNF[A](s: Set[Set[A]]) def trueCNF[A] = CNF[A](Set()) def falseCNF[A] = CNF[A](Set(Set())) \end{lstlisting} -\vspace{-0.8\baselineskip} -\end{wrapfigure}% The value \lstinline!false! should have the property that for any group $g$ of disjunctions, e.g., \lstinline!g == (x || y)!, we must @@ -685,21 +668,15 @@ \subsubsection{Example \label{subsec:Example-list-monads-7}\ref{subsec:Example-l represents an empty disjunction and stands for the constant \lstinline!false!, while the constant \lstinline!true! is \lstinline!Set(Set())!, a disjunction containing an empty conjunction. - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.6\baselineskip} \begin{lstlisting} final case class DNF[A](s: Set[Set[A]]) def trueDNF[A] = DNF[A](Set(Set())) def falseDNF[A] = DNF[A](Set()) \end{lstlisting} -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent It is easy to make mistakes when reasoning about the constants -\lstinline!true! and \lstinline!false! in these representations. -We need to test the code to make sure our implementations of \lstinline!true! -and \lstinline!false! are correct. +It is easy to make a mistake when setting the constants \lstinline!true! +and \lstinline!false! in these representations. We need to test the +code to make sure our implementations of \lstinline!true! and \lstinline!false! +are correct. Before writing the full code, let us implement the DNF-to-CNF conversion for a short example: @@ -818,8 +795,6 @@ \subsubsection{Example \label{subsec:Example-matrix-products}\ref{subsec:Example \textbf{(a)} If a matrix has type \lstinline!Seq[Seq[A]]!, a transposed matrix\index{matrix transposition} has the same type. An example: -\begin{wrapfigure}{l}{0.64\columnwidth}% -\vspace{-0.85\baselineskip} \begin{minipage}[t]{0.45\linewidth}% \begin{lstlisting} val matrix: Seq[Seq[Int]] = @@ -840,14 +815,13 @@ \subsubsection{Example \label{subsec:Example-matrix-products}\ref{subsec:Example ) \end{lstlisting} % -\end{minipage} ~ \vspace{-0.95\baselineskip} -\end{wrapfigure}% +\end{minipage} -\noindent To compute the transposed matrix, we have to iterate over -the initial matrix. A functor block of type \lstinline!Seq! will -return a value of type \lstinline!Seq[Seq[A]]! only if the \lstinline!yield! -expression itself returns a value of type \lstinline!Seq[A]!. The -example above shows that the first iteration should return \lstinline!Seq(1, 10)!, +To compute the transposed matrix, we have to iterate over the initial +matrix. A functor block of type \lstinline!Seq! will return a value +of type \lstinline!Seq[Seq[A]]! only if the \lstinline!yield! expression +itself returns a value of type \lstinline!Seq[A]!. The example above +shows that the first iteration should return \lstinline!Seq(1, 10)!, which contains the first elements of all inner sequences from \lstinline!matrix!. The second iteration should return all second elements, and so on. We see that we need to iterate over the indices of the matrix columns. @@ -1023,7 +997,7 @@ \subsection{Pass/fail monads\label{subsec:Pass/fail-monads}} y = f(x) // No possibility of failure in this line. if p(y) // The entire expression will fail if `p(y) == false`. z <- Try(g(x, y)) // The computation may also fail here. - r <- Try(h(x, y, z)) // May fail here as well. + r <- Try(h(x, y, z)) // And here. } yield r // If `r` has type `A` then `result` has type `Try[A]`. \end{lstlisting} The function \lstinline!Try()! catches exceptions thrown by its argument. @@ -1052,11 +1026,11 @@ \subsection{Pass/fail monads\label{subsec:Pass/fail-monads}} def sqrt(x: Int): Result = if (x < 0) Left(s"error: sqrt($x)") else Right(math.sqrt(x).toInt) val initial: Result = Right(20) // Start with a given `initial` value of type `Result`. -scala> val result: Result = for { // Safe computation: `sqrt(1000 / initial - 100) + 20`. +scala> val result: Result = for { // Compute `sqrt(1000 / initial - 100) + 10`. x <- initial y <- div(1000, x) z <- sqrt(y - 100) -} yield z + 1 +} yield z + 10 result: Result = Left("error: sqrt(-50)") \end{lstlisting} The concise and readable code of \lstinline!val result! replaces @@ -1076,7 +1050,7 @@ \subsection{Pass/fail monads\label{subsec:Pass/fail-monads}} that may fail; the first failure is then returned as a value. \subsubsection{Example \label{subsec:Example-:chain-with-option}\ref{subsec:Example-:chain-with-option}: -chaining computations with \texttt{Option}\index{solved examples}} +chaining computations with \texttt{Option}\index{examples (with code)}} Some clients have placed some orders with some companies. The information is made available via Java system properties, for example: @@ -1328,7 +1302,7 @@ \subsection{Tree-like semi-monads and monads\label{subsec:Tree-like-semimonads-a The following examples show some use cases for tree-like monads. -\subsubsection{Example \label{subsec:Example-monad-branching-properties}\ref{subsec:Example-monad-branching-properties}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-monad-branching-properties}\ref{subsec:Example-monad-branching-properties}\index{examples (with code)}} Implement the \lstinline!flatMap! operation for a tree of configuration properties of the form: @@ -1761,11 +1735,10 @@ \subsection{The \texttt{Writer} monad} } } \end{lstlisting} -\begin{comment} +In the code notation: \[ -\text{flm}_{\text{Write}}(f^{:A\rightarrow B\times W})\triangleq a\times w\rightarrow\left(\pi_{1}f(a)\right)\times\left(w\oplus\pi_{2}f(a)\right)\quad. +\text{flm}_{\text{Write}}(f^{:A\rightarrow B\times W})\triangleq a\times w\rightarrow\left(f(a)\triangleright\pi_{1}\right)\times\left(w\oplus f(a)\triangleright\pi_{2}\right)\quad. \] -\end{comment} The logging type $W$ is often a monoid (a semigroup with an \textsf{``}empty\textsf{''} value). If so, \lstinline!Writer[A, W]! will be a full monad whose @@ -1838,13 +1811,13 @@ \subsection{The \texttt{State} monad\label{subsec:The-State-monad}} \left(A\times S\rightarrow B\times S\right)\cong\left(A\rightarrow S\rightarrow B\times S\right)=A\rightarrow L^{B}\quad,\quad\text{where}\quad L^{B}\triangleq S\rightarrow B\times S\quad. \] So, the\index{monads!State monad@\texttt{State} monad} \lstinline!State! -monad must be defined by the type constructor $\text{State}^{S,A}\triangleq S\rightarrow A\times S$. +monad is defined by the type constructor $\text{State}^{S,A}\triangleq S\rightarrow A\times S$. This is a function that computes a value of type $A$ while using -and possibly updating the \textsf{``}state value\textsf{''} of type $S$. +and possibly updating the \textsf{``}internal state value\textsf{''} of type $S$. -To code of the \lstinline!flatMap! method for this type constructor -was derived in Example~\ref{subsec:ch-solvedExample-9}(c). It does -indeed pass the updated state value to the next computation: +The code of the \lstinline!flatMap! method for this type constructor +was derived in Example~\ref{subsec:ch-solvedExample-9}(c). The code +passes the updated state value to the next computation: \begin{lstlisting} type State[S, A] = S => (A, S) def flatMap[S, A, B](prev: State[S, A])(f: A => State[S, B]): State[S, B] = { s => @@ -1853,7 +1826,7 @@ \subsection{The \texttt{State} monad\label{subsec:The-State-monad}} } \end{lstlisting} -An example of using the \lstinline!State! monad is the task of implementing +An example of using the \lstinline!State! monad is when implementing a random number generator. A simple generator is the \textbf{Lehmer\textsf{'}s algorithm}\index{Lehmer\textsf{'}s algorithm},\footnote{See \texttt{\href{https://en.wikipedia.org/wiki/Lehmer_random_number_generator}{https://en.wikipedia.org/wiki/Lehmer\_random\_number\_generator}}} which generates integer sequences $x_{n}$ defined by: @@ -1863,21 +1836,18 @@ \subsection{The \texttt{State} monad\label{subsec:The-State-monad}} The \textsf{``}updating\textsf{''} function for this sequence, $x_{n+1}=\text{lehmer}\,(x_{n})$, can be implemented as: \begin{lstlisting} -def lehmer(x: Long): Long = x * 48271L % ((1L << 31) - 1) +def lehmer(x: Long): Long = (x * 48271L) % ((1L << 31) - 1L) \end{lstlisting} In many applications, one needs uniformly distributed floating-point numbers in the interval $\left[0,1\right]$. To produce such numbers, let us define a helper function: \begin{lstlisting} -def uniform(x: Long): Double = (x - 1).toDouble / ((1L << 31) - 3) // Enforce the interval [0, 1]. +def uniform(x: Long): Double = (x - 1L).toDouble / ((1L << 31) - 3L) // Enforce the interval [0, 1]. \end{lstlisting} To use the uniform generator, we need to provide an initial value $x_{0}$ (the \textsf{``}seed\textsf{''}) and then call the function \lstinline!lehmer! repeatedly on successive values. The code would look like this: - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} val s0 = 123456789L // A "seed" value. val s1 = lehmer(s0) @@ -1888,11 +1858,7 @@ \subsection{The \texttt{State} monad\label{subsec:The-State-monad}} ... // Use pseudo-random value r2. val s3 = lehmer(s2) // And so on. \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent We need to keep track of the generator\textsf{'}s state values \lstinline!s1!, +We need to keep track of the generator\textsf{'}s state values \lstinline!s1!, \lstinline!s2!, ..., that are not directly needed for other computations. This \textsf{``}bookkeeping\textsf{''} is error-prone since we might reuse a previous generator state by mistake. The \lstinline!State! monad keeps track @@ -1906,7 +1872,7 @@ \subsection{The \texttt{State} monad\label{subsec:The-State-monad}} generator is implemented as a monadic value of type \lstinline!State[Long, Double]!: \begin{lstlisting} val rngUniform: State[Long, Double] = { oldState => - val result = uniform(oldState) // Enforce the interval [0, 1]. + val result = uniform(oldState) val newState = lehmer(oldState) (result, newState) } @@ -1984,9 +1950,6 @@ \subsection{The eager/lazy evaluation monad\label{subsec:The-eager-lazy-evaluati Assuming that \lstinline!map! and \lstinline!flatMap! are defined as extension methods for \lstinline!Eval!, we may use functor blocks to combine eager and lazy computations freely: - -\begin{wrapfigure}{l}{0.631\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} val result: Eval[Int] = for { x <- later(longComputation1()) // Delay the long computation. @@ -1994,16 +1957,11 @@ \subsection{The eager/lazy evaluation monad\label{subsec:The-eager-lazy-evaluati z <- later(longComputation2(y * 100)) } yield z \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent The value of \lstinline!result! is a lazy computation because -it involves lazy steps. It is quick to compute \lstinline!result! -because the long computations are not yet started. To extract the -final value of type \lstinline!Int! out of \lstinline!result!, we -need to evaluate \lstinline!get(result)!, which will take a longer -time. +The value of \lstinline!result! is a lazy computation because it +involves lazy steps. It is quick to compute \lstinline!result! because +the long computations are not yet started. To extract the final value +of type \lstinline!Int! out of \lstinline!result!, we need to evaluate +\lstinline!get(result)!, which will take a longer time. \subsection{The continuation monad\label{subsec:The-continuation-monad}} @@ -2030,9 +1988,6 @@ \subsection{The continuation monad\label{subsec:The-continuation-monad}} \end{lstlisting} To make the pattern more clear, replace the constant \lstinline!10! by a function \lstinline!pure! with a callback argument: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} def pure(x: Int)(callback: Int => Unit): Unit = callback(x) @@ -2046,17 +2001,16 @@ \subsection{The continuation monad\label{subsec:The-continuation-monad}} } } \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent This code is a typical pattern of continuation-passing style. -The final result of the calculation is only available as the bound -variable \lstinline!z! in a deeply nested function scope. This makes -working with this code style more difficult: We can continue the program -only by writing code either directly within that deeply nested scope -or within the given \lstinline!callback!. This is the heuristic reason -why callback programming is known as \textsf{``}continuation-passing\textsf{''} style. +This code illustrates the continuation-passing style: each step of +the calculation calls a function (a \textsf{``}continuation\textsf{''}) passed to +it as the last curried argument. The final result of the calculation +is not returned by the function \lstinline!result! but is available +only as the bound variable \lstinline!z! in a deeply nested function +scope where \lstinline!callback(z)! is run. If we need to extend +this program with some more calculation steps, we would have to add +extra code within the deepest-nested scope of \lstinline!result!, +or we could pass a \lstinline!callback! argument that contains further +steps. Another feature of the continuation-passing style is that callbacks could be called at a later time, concurrently with the main thread @@ -2081,29 +2035,28 @@ \subsection{The continuation monad\label{subsec:The-continuation-monad}} to write and to understand. One complication is that it is difficult to maintain code that contains -deeply nested function scopes. The continuation monad solves this +deeply nested function scopes. The continuation monad solves that problem by converting nested function scopes into more easily composable functor blocks. The code also becomes more readable. To derive the required type constructor, consider a computation of -type $A\rightarrow B$ that needs to use the continuation-passing +type $A\rightarrow B$ that needs to be converted to the continuation-passing style. Instead of returning a value of type $B$, it registers a callback -function for possible later use and returns a \lstinline!Unit! value. -If the callback has type $B\rightarrow\bbnum 1$, the total input -of our computation is $A\times\left(B\rightarrow\bbnum 1\right)$ -while the output is simply $\bbnum 1$. So, the type of a continuation-passing -computation is $A\times\left(B\rightarrow\bbnum 1\right)\rightarrow\bbnum 1$. -Rewrite this type in the form $A\rightarrow L^{B}$ with a suitable -functor $L$: +function and returns a \lstinline!Unit! value. If the callback has +type $B\rightarrow\bbnum 1$, the total input of our computation is +$A\times\left(B\rightarrow\bbnum 1\right)$ while the output is simply +$\bbnum 1$. So, the type of a continuation-passing computation is +$A\times\left(B\rightarrow\bbnum 1\right)\rightarrow\bbnum 1$. Rewrite +this type in the form $A\rightarrow L^{B}$ with a suitable functor +$L$: \[ \left(A\times\left(B\rightarrow\bbnum 1\right)\rightarrow\bbnum 1\right)\cong\left(A\rightarrow\left(B\rightarrow\bbnum 1\right)\rightarrow\bbnum 1\right)=A\rightarrow L^{B}\quad,\quad\text{where}\quad L^{A}\triangleq\left(A\rightarrow\bbnum 1\right)\rightarrow\bbnum 1\quad. \] + It is sometimes helpful if the callback returns a more informative value than \lstinline!Unit!. For instance, that value could show error information or give access to processes that were scheduled -concurrently. - -So, we generalize the type constructor $L$ to $\left(A\rightarrow R\right)\rightarrow R$, +concurrently. So, we generalize the type constructor $L^{A}$ to $\left(A\rightarrow R\right)\rightarrow R$, where $R$ is a fixed \textsf{``}result\textsf{''} type. This type constructor is called the \textbf{continuation monad}\index{monads!continuation monad (Cont)@continuation monad (\texttt{Cont})} and is denoted by $\text{Cont}^{R,A}\triangleq\left(A\rightarrow R\right)\rightarrow R$. @@ -2115,10 +2068,10 @@ \subsection{The continuation monad\label{subsec:The-continuation-monad}} type Cont[R, A] = (A => R) => R def flatMap[R, A, B](ca: Cont[R, A])(f: A => Cont[R, B]): Cont[R, B] = { br => ca(a => f(a)(br)) } \end{lstlisting} -The code of \lstinline!flatMap! substitutes a new callback, \lstinline!br: B => R!, +The code of \lstinline!flatMap! substitutes a new callback (\lstinline!br: B => R!) into the innermost scope of the computation \lstinline!f!. In this -way, we obtain easy access to the innermost callback scope. This trick -makes code with deeply nested callbacks composable. +way, we obtain direct access to the innermost callback scope. This +trick makes code with deeply nested callbacks composable. After defining \lstinline!map! and \lstinline!flatMap! as extension methods on \lstinline!Cont!, we can rewrite the code above as: @@ -2141,9 +2094,6 @@ \subsection{The continuation monad\label{subsec:The-continuation-monad}} a runner.\index{monads!runner} One way of extracting values from \lstinline!Cont!-monadic programs is to use a runner producing a \lstinline!Future! value that resolves when the callback is called: - -\begin{wrapfigure}{l}{0.515\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} def runner[A](c: Cont[Unit, A]): Future[A] = { val pr = Promise[A]() // scala.concurrent.Promise @@ -2153,11 +2103,7 @@ \subsection{The continuation monad\label{subsec:The-continuation-monad}} // Wait for the Future value. Await.result(runner(result), Duration.Inf) \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent This runner uses special low-level features of the \lstinline!Future! +This runner uses special low-level features of the \lstinline!Future! class, such as a mutable value of type \lstinline!Promise!. If these features are used in many places in the code, the programmer risks creating concurrency bugs, such as race conditions or deadlocks, that @@ -2169,12 +2115,12 @@ \subsection{The continuation monad\label{subsec:The-continuation-monad}} We conclude this subsection with some more examples of using the continuation monad. -\subsubsection{Example \label{subsec:Example-continuation-monad-computation-cost}\ref{subsec:Example-continuation-monad-computation-cost}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-continuation-monad-computation-cost}\ref{subsec:Example-continuation-monad-computation-cost}\index{examples (with code)}} -Each arithmetic computation, such as \lstinline!add3! or \lstinline!mult4!, -now has a certain cost, which is a value of a monoid type $W$. Use -the monad \lstinline!Cont[W, A]! to implement computations with a -specified cost. The total cost must add up automatically when computations +Suppose that each arithmetic computation, such as \lstinline!add3! +or \lstinline!mult4!, has a certain cost, which is a value of a monoid +type $W$. Use the monad \lstinline!Cont[W, A]! to implement computations +with a cost. The total cost must add up automatically when computations are chained using \lstinline!flatMap!. \subparagraph{Solution} @@ -2208,7 +2154,7 @@ \subsubsection{Example \label{subsec:Example-continuation-monad-computation-cost val resultInt = Await.result(futureResult, Duration.Inf) // Wait for the Future value. \end{lstlisting} -If the code contains many other monadic operations such as \lstinline!add3! +If the code contains many monadic operations similar to \lstinline!add3! and \lstinline!mult4!, it is inconvenient to hard-code the cost each time. Instead, we can easily implement a function that adds a given cost to any given monadic operation: @@ -4148,7 +4094,7 @@ \subsubsection{Exercise \label{subsec:Exercise-monads-p1-7}\ref{subsec:Exercise- \section{Laws of semi-monads and monads} -\subsection{Motivation for the semi-monad laws} +\subsection{Motivating the semi-monad laws} This chapter introduced semi-monads to encode nested iteration as functor block programs with multiple source lines. What properties @@ -4174,7 +4120,7 @@ \subsection{Motivation for the semi-monad laws} y = f(x) z <- g(y) // Same as z <- g(f(x)). } yield z - // Translating the functor block into methods: + // Rewrite via method chains: val result1 = c.flatMap(x => g(f(x))) \end{lstlisting} % @@ -4184,9 +4130,9 @@ \subsection{Motivation for the semi-monad laws} val result2 = for { y <- c.map(x => f(x)) - z <- g(y) // This code is unchanged. + z <- g(y) // This is unchanged. } yield z - // Translating the functor block into methods: + // Rewrite via method chains: val result2 = c.map(f).flatMap(y => g(y)) \end{lstlisting} % @@ -4210,7 +4156,7 @@ \subsection{Motivation for the semi-monad laws} z <- f(x) y = g(z) } yield y - // Translating the functor block into methods: + // Rewrite via method chains: val result1 = c.flatMap(f).map(g) \end{lstlisting} % @@ -4222,7 +4168,7 @@ \subsection{Motivation for the semi-monad laws} y <- f(x).map(z => g(z)) } yield y - // Translating the functor block into methods: + // Rewrite via method chains: val result2 = c.flatMap { x => f(x).map(g) } \end{lstlisting} % @@ -4244,7 +4190,7 @@ \subsection{Motivation for the semi-monad laws} y <- f(x) z <- g(y) } yield z - // Translating the functor block into methods: + // Rewrite via method chains: val result1 = c.flatMap{ x => f(x).flatMap(g) } \end{lstlisting} % @@ -4256,7 +4202,7 @@ \subsection{Motivation for the semi-monad laws} y <- f(x) } yield y z <- g(yy) } yield z - // Translating the functor block into methods: + // Rewrite via method chains: val result2 = c.flatMap(f).flatMap(yy => g(yy)) \end{lstlisting} % @@ -4285,26 +4231,16 @@ \subsection{Motivation for the semi-monad laws} \text{flm}^{A,B}:(A\rightarrow S^{B})\rightarrow S^{A}\rightarrow S^{B}\quad. \] The first law is written in Scala code as: - -\begin{wrapfigure}{l}{0.37\columnwidth}% -\vspace{-0.75\baselineskip} - \begin{lstlisting} -c.flatMap(x => g(f(x))) == - c.map(f).flatMap(g) +c.flatMap(x => g(f(x))) == c.map(f).flatMap(g) \end{lstlisting} -\vspace{0.2\baselineskip} \[ \xymatrix{\xyScaleY{0.2pc}\xyScaleX{3pc} & S^{B}\ar[rd]\sp(0.5){~~\ \text{flm}\,(g^{:B\rightarrow S^{C}})}\\ S^{A}\ar[ru]\sp(0.5){f^{\uparrow S}}\ar[rr]\sb(0.5){\text{flm}\,(f^{:A\rightarrow B}\bef\,g^{:B\rightarrow S^{C}})\,} & & S^{C} } \] - -\vspace{-0.6\baselineskip} -\end{wrapfigure}% - -\noindent In the code notation, we write this law in the point-free -style\index{point-free style} by omitting the argument \lstinline!c!: +In the code notation, we write this law in the point-free style\index{point-free style} +by omitting the argument \lstinline!c!: \begin{equation} \text{flm}\,(f^{:A\rightarrow B}\bef g^{:B\rightarrow S^{C}})=f^{\uparrow S}\bef\text{flm}\,(g)\quad.\label{eq:left-naturality-law-flatMap} @@ -4321,25 +4257,15 @@ \subsection{Motivation for the semi-monad laws} The second law holds for arbitrary $f^{:A\rightarrow S^{B}}$ and $g^{:B\rightarrow C}$: - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.75\baselineskip} - \begin{lstlisting} -c.flatMap(f).map(g) == - c.flatMap { x => f(x).map(g) } +c.flatMap(f).map(g) == c.flatMap { x => f(x).map(g) } \end{lstlisting} -\vspace{0.2\baselineskip} \[ \xymatrix{\xyScaleY{0.2pc}\xyScaleX{3pc} & S^{B}\ar[rd]\sp(0.5){\ \,(g^{:B\rightarrow C})^{\uparrow S}}\\ S^{A}\ar[ru]\sp(0.5){\text{flm}\,(f^{:A\rightarrow S^{B}})~~~}\ar[rr]\sb(0.5){\text{flm}\,(f^{:A\rightarrow S^{B}}\bef\,g^{\uparrow S})\,} & & S^{C} } \] -\vspace{-0.2\baselineskip} -\end{wrapfigure}% - -\noindent \vspace{-0.35\baselineskip} \begin{equation} \text{flm}\,(f^{:A\rightarrow S^{B}}\bef g^{\uparrow S})=\text{flm}\,(f)\bef g^{\uparrow S}\quad.\label{eq:right-naturality-law-flatMap} \end{equation} @@ -4351,31 +4277,17 @@ \subsection{Motivation for the semi-monad laws} \lstinline!B!. The third law relates nested \lstinline!flatMap! operations: - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.6\baselineskip} - \begin{lstlisting} -c.flatMap { x => f(x).flatMap(g) } == - c.flatMap(f).flatMap(g) +c.flatMap { x => f(x).flatMap(g) } == c.flatMap(f).flatMap(g) \end{lstlisting} -\vspace{0\baselineskip} \[ \xymatrix{\xyScaleY{0.2pc}\xyScaleX{3pc} & S^{B}\ar[rd]\sp(0.5){~\ \text{flm}\,(g^{:B\rightarrow S^{C}})}\\ S^{A}\ar[ru]\sp(0.5){\text{flm}\,(f^{:A\rightarrow S^{B}})~~\ }\ar[rr]\sb(0.5){\text{flm}\,(f^{:A\rightarrow S^{B}}\bef\,\text{flm}\,(g))\,} & & S^{C} } \] - -\vspace{0.6\baselineskip} -\end{wrapfigure}% -~\vspace{-1.6\baselineskip} - \begin{equation} \text{flm}\,\big(f^{:A\rightarrow S^{B}}\bef\text{flm}\,(g^{:B\rightarrow S^{C}})\big)=\text{flm}\left(f\right)\bef\text{flm}\left(g\right)\quad.\label{eq:associativity-law-flatMap} \end{equation} - -\vspace{-0.2\baselineskip} - This equation is called the \textbf{associativity law}\index{associativity law!of flatMap@of \texttt{flatMap}} of \lstinline!flatMap!, for reasons we will explain later. @@ -4421,24 +4333,14 @@ \subsection{The laws of \texttt{flatten}} By definition, \lstinline!flatMap! is expressed as a composition of \lstinline!map! and \lstinline!flatten!: - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.85\baselineskip} - \begin{lstlisting} c.flatMap(f) == c.map(f).flatten \end{lstlisting} -\vspace{0.2\baselineskip} \[ \xymatrix{\xyScaleY{0.3pc}\xyScaleX{3.5pc} & S^{S^{B}}\ar[rd]\sp(0.5){\ \text{ftn}\ }\\ S^{A}\ar[ru]\sp(0.5){(f^{:A\rightarrow S^{B}})^{\uparrow S}\ }\ar[rr]\sp(0.5){\text{flm}\,(f^{:A\rightarrow S^{B}})\,} & & S^{B} } \] - -\vspace{1.3\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.5\baselineskip} \[ \text{flm}_{S}(f)=f^{\uparrow S}\bef\text{ftn}_{S}\quad. \] @@ -4592,7 +4494,7 @@ \subsection{Verifying the associativity law via \texttt{flatten}} The following examples will verify the associativity law of \lstinline!flatten! for some standard monads. -\subsubsection{Example \label{subsec:Example-flatten-verify-for-monad}\ref{subsec:Example-flatten-verify-for-monad}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-flatten-verify-for-monad}\ref{subsec:Example-flatten-verify-for-monad}\index{examples (with code)}} The standard Scala types \lstinline!Either! and \lstinline!Try! are examples of the monad $F^{A}\triangleq Z+A$, where $Z$ is a @@ -4602,9 +4504,6 @@ \subsubsection{Example \label{subsec:Example-flatten-verify-for-monad}\ref{subse The type signature of \lstinline!flatten! is $\text{ftn}:Z+\left(Z+A\right)\rightarrow Z+A$, and its code is: - -\begin{wrapfigure}{l}{0.6\columnwidth}% -\vspace{-0.2\baselineskip} \begin{lstlisting} def flatten[A]: Either[Z, Either[Z, A]] => Either[Z, A] = { case Left(z) => Left(z) @@ -4612,11 +4511,6 @@ \subsubsection{Example \label{subsec:Example-flatten-verify-for-monad}\ref{subse case Right(Right(a)) => Right(a) } \end{lstlisting} - -\vspace{-3.2\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.2\baselineskip} \[ \text{ftn}^{:Z+Z+A\rightarrow Z+A}\triangleq\,\begin{array}{|c||cc|} & Z & A\\ @@ -4625,16 +4519,15 @@ \subsubsection{Example \label{subsec:Example-flatten-verify-for-monad}\ref{subse A & \bbnum 0 & \text{id} \end{array}\quad. \] -\vspace{-0.8\baselineskip} Since \lstinline!flatten! is fully parametric, both sides of the -law are fully parametric functions with the type signature $Z+Z+Z+A\rightarrow Z+A$. -This type signature has \emph{only one} fully parametric implementation: -since it is not possible to produce values of unknown types $A$ and -$Z$ from scratch, an implementation of $Z+Z+Z+A\rightarrow Z+A$ -must return $Z+\bbnum 0$ when the input contains a value of type -$Z$; otherwise it must return $\bbnum 0+A$. So, both sides of the -law must have the same code. +associativity law are fully parametric functions with the type signature +$Z+Z+Z+A\rightarrow Z+A$. This type signature has \emph{only one} +fully parametric implementation: since it is not possible to produce +values of unknown types $A$ and $Z$ from scratch, an implementation +of $Z+Z+Z+A\rightarrow Z+A$ must return $Z+\bbnum 0$ when the input +contains a value of type $Z$; otherwise it must return $\bbnum 0+A$. +So, both sides of the law must have the same code. To make this argument rigorous, we may use the Curry-Howard correspondence and the LJT algorithm (see Section~\ref{app:The-LJT-algorithm}). @@ -4944,7 +4837,7 @@ \subsubsection{Example \label{subsec:Example-flatten-verify-for-monad-4}\ref{sub Had the code not exchanged the order of $w_{1}$ and $w_{2}$, the law would have held. -\subsection{From semi-monads to monads: Motivation for the identity laws} +\subsection{From semi-monads to monads: Motivating the identity laws} Semi-monads are heuristically viewed as values with a special \textsf{``}computational effect\textsf{''}. Semi-monad-valued computations can be composed using the @@ -4956,14 +4849,14 @@ \subsection{From semi-monads to monads: Motivation for the identity laws} (notation $\text{pu}_{M}$): \begin{wrapfigure}{l}{0.475\columnwidth}% -\vspace{-0.8\baselineskip} +\vspace{-0.4\baselineskip} \begin{lstlisting} def pure[A](a: A): M[A] \end{lstlisting} \vspace{-0.6\baselineskip} \end{wrapfigure}% -~\vspace{-0.5\baselineskip} +~\vspace{-0.4\baselineskip} \[ \text{pu}_{M}^{A}:A\rightarrow M^{A}\quad. \] @@ -4975,12 +4868,8 @@ \subsection{From semi-monads to monads: Motivation for the identity laws} no iteration. In a functor block, this intuition says that a source line with an \textsf{``}empty effect\textsf{''}, \lstinline!y <- pure(x)!, should be equivalent to just \lstinline!y = x!. This line must occur either -before or after another source line, for instance: - -\begin{comment} -So, we need to examine two possibilities: first, an empty effect comes -before another source line, -\end{comment} +before or after another source line. So, we need to examine two situations: +first, when an empty effect comes before another source line: \noindent \texttt{\textcolor{blue}{\footnotesize{}}}% \begin{minipage}[c]{0.475\columnwidth}% @@ -4988,9 +4877,9 @@ \subsection{From semi-monads to monads: Motivation for the identity laws} \begin{lstlisting} result1 = for { ... // Some code, then: - y <- pure(x) // "Empty effect" with x: A. - z <- someArray(y) // someArray: A => M[B] - // Same as z <- pure(x).flatMap(someArray) + y <- pure(x) // Empty effect, x:A + z <- p(y) // p: A => M[B] + // Same as z <- pure(x).flatMap(p) \end{lstlisting} % \end{minipage}\texttt{\textcolor{blue}{\footnotesize{}\hspace*{\fill}}}% @@ -4999,9 +4888,9 @@ \subsection{From semi-monads to monads: Motivation for the identity laws} \begin{lstlisting} result2 = for { ... // Some code, then: - y = x // x: A - z <- someArray(y) // someArray: A => M[B] - // Same as z <- someArray(x) + y = x // x: A + z <- p(y) // p: A => M[B] + // Same as z <- p(x) \end{lstlisting} % \end{minipage}{\footnotesize\par} @@ -5009,32 +4898,24 @@ \subsection{From semi-monads to monads: Motivation for the identity laws} \noindent \vspace{0.1\baselineskip} The equality of \lstinline!result1! and \lstinline!result2! gives the following law: for all $g^{:A\rightarrow M^{B}}$, - -\begin{wrapfigure}{l}{0.3\columnwidth}% -\vspace{-0.9\baselineskip} \begin{lstlisting} pure(x).flatMap(g) == g(x) \end{lstlisting} -\vspace{-0.6\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.4\baselineskip} \begin{align} {\color{greenunder}\text{left identity law of }M:}\quad & \text{pu}_{M}\bef\text{flm}_{M}(g^{:A\rightarrow M^{B}})=g\quad.\label{eq:monad-left-identity-law-for-flatMap} \end{align} -\vspace{-1.2\baselineskip} -The second possibility is that an empty effect comes \emph{after} -a source line: +The second situation is when an empty effect comes \emph{after} a +source line: \noindent \texttt{\textcolor{blue}{\footnotesize{}}}% \begin{minipage}[c]{0.475\columnwidth}% \texttt{\textcolor{blue}{\footnotesize{}}} \begin{lstlisting} result1 = for { - x <- someArray // someArray: M[A] - y <- pure(x) // Empty effect with x: A. -// Same as y <- someArray.flatMap(x => pure(x)) + x <- p // p: M[A] + y <- pure(x) // Empty effect. +// Same as y <- p.flatMap(x => pure(x)) \end{lstlisting} % \end{minipage}\texttt{\textcolor{blue}{\footnotesize{}\hspace*{\fill}}}% @@ -5042,9 +4923,9 @@ \subsection{From semi-monads to monads: Motivation for the identity laws} \texttt{\textcolor{blue}{\footnotesize{}}} \begin{lstlisting} result2 = for { - x <- someArray // someArray: M[A] + x <- someArray // p: M[A] y = x - // Same as y <- someArray + // Same as y <- p \end{lstlisting} % \end{minipage}{\footnotesize\par} @@ -5052,16 +4933,9 @@ \subsection{From semi-monads to monads: Motivation for the identity laws} \noindent \vspace{0.1\baselineskip} Then the equality of \lstinline!result1! and \lstinline!result2! gives the law: - -\begin{wrapfigure}{l}{0.3\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} g.flatMap(pure) == g \end{lstlisting} -\vspace{-0.6\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.4\baselineskip} \begin{align} {\color{greenunder}\text{right identity law of }M:}\quad & \text{flm}_{M}(\text{pu}_{M})=\text{id}^{:M^{A}\rightarrow M^{A}}\quad.\label{eq:monad-right-identity-law-for-flatMap} \end{align} @@ -5111,18 +4985,11 @@ \subsection{The monad identity laws in terms of \texttt{pure} and \texttt{flatte \] Begin with the left identity law of \lstinline!flatMap!, written as: - -\begin{wrapfigure}{l}{0.32\columnwidth}% -\vspace{-0\baselineskip} \[ \xymatrix{\xyScaleY{1.0pc}\xyScaleX{3pc} & M^{M^{A}}\ar[rd]\sp(0.5){\ \text{ftn}^{A}}\\ M^{A}\ar[ru]\sp(0.5){\text{pu}^{M^{A}}}\ar[rr]\sb(0.5){\text{id}} & & M^{A} } \] -\vspace{-0.2\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.8\baselineskip} \[ \text{pu}_{M}\bef\text{flm}_{M}(f)=f\quad. \] @@ -5141,24 +5008,16 @@ \subsection{The monad identity laws in terms of \texttt{pure} and \texttt{flatte The \index{identity laws!of pure and flatten@of \texttt{pure} and \texttt{flatten}}\textbf{right identity law} of \lstinline!flatten! is written as: - -\begin{wrapfigure}{l}{0.32\columnwidth}% -\vspace{-1.6\baselineskip} \[ \xymatrix{\xyScaleY{0.2pc}\xyScaleX{3pc} & M^{M^{A}}\ar[rd]\sp(0.5){\ \text{ftn}^{A}\ }\\ M^{A}\ar[ru]\sp(0.5){(\text{pu}^{A})^{\uparrow M}\quad}\ar[rr]\sb(0.5){\text{id}} & & M^{A} } \] -\vspace{-1\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.6\baselineskip} \begin{align} & \text{flm}_{M}(\text{pu}_{M})=\text{pu}_{M}^{\uparrow M}\bef\text{ftn}_{M}\overset{!}{=}\text{id}\quad.\label{eq:right-identity-law-for-flatten} \end{align} -\vspace{-0.1\baselineskip} -In the next section, we will see a reason why these laws have their +In the next section, we will see reasons why these laws have their names. \subsection{Monad laws in terms of Kleisli functions} @@ -5184,24 +5043,16 @@ \subsection{Monad laws in terms of Kleisli functions} This resembles the forward composition of ordinary functions, $\left(\bef\right):\left(A\rightarrow B\right)\rightarrow\left(B\rightarrow C\right)\rightarrow A\rightarrow C$, except for different types of functions. If $M$ is a monad, the implementation of $\diamond_{_{M}}$ is: - -\begin{wrapfigure}{l}{0.67\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} def <>[M[_]: Monad, A,B,C](f: A => M[B], g: B => M[C]): A => M[C] = { x => f(x).flatMap(g) } \end{lstlisting} -\vspace{-0.9\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.8\baselineskip} \begin{equation} f\diamond_{_{M}}g\triangleq f\bef\text{flm}_{M}(g)\quad.\label{eq:def-of-kleisli-composition-for-monad-via-flatMap} \end{equation} -\vspace{-0.5\baselineskip} The Kleisli composition can be equivalently expressed by a functor -block code as: +block: \begin{lstlisting}[mathescape=true] def <>[M[_]: Monad, A,B,C](f: A => M[B], g: B => M[C]): A => M[C] = { x => @@ -5255,22 +5106,14 @@ \subsubsection{Statement \label{subsec:Statement-flatMap-lifting-composition-law For a lawful monad $M$, the \lstinline!flatMap! method satisfies the composition law\index{composition law!of flatMap@of \texttt{flatMap}}: - -\begin{wrapfigure}{l}{0.32\columnwidth}% -\vspace{-1.5\baselineskip} \[ \xymatrix{\xyScaleY{0.8pc}\xyScaleX{3pc} & M^{B}\ar[rd]\sp(0.5){\ \text{flm}_{M}(g)\ }\\ M^{A}\ar[ru]\sp(0.5){\text{flm}_{M}(f)\quad}\ar[rr]\sb(0.5){\text{flm}_{M}(f\diamond_{_{_{M}}}g)} & & M^{C} } \] -\vspace{0.1\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.2\baselineskip} \[ \text{flm}_{M}(f\diamond_{_{_{M}}}g)=\text{flm}_{M}(f)\bef\text{flm}_{M}(g)\quad. \] -\vspace{-0.8\baselineskip} \subparagraph{Proof} @@ -5315,10 +5158,6 @@ \subsubsection{Statement \label{subsec:Statement-associativity-law-for-kleisli}\ Since the Kleisli composition describes the chaining of consecutive lines in functor blocks, its associativity means that multiple lines are chained unambiguously. For example, this code: - -\begin{wrapfigure}{l}{0.14\columnwidth}% -\vspace{0.1\baselineskip} - \begin{lstlisting}[numbers=left] x => for { y <- f(x) @@ -5326,16 +5165,13 @@ \subsubsection{Statement \label{subsec:Statement-associativity-law-for-kleisli}\ t <- h(z) } yield t \end{lstlisting} -\vspace{0\baselineskip} -\end{wrapfigure}% - -\noindent corresponds to the Kleisli composition:\vspace{-0.2\baselineskip} +corresponds to this Kleisli composition: \[ -(x\rightarrow f(x))\diamond_{_{_{M}}}(y\rightarrow g(y))\diamond_{_{_{M}}}(z\rightarrow h(z)) +(x\rightarrow f(x))\diamond_{_{_{M}}}(y\rightarrow g(y))\diamond_{_{_{M}}}(z\rightarrow h(z))\quad. \] -and does not need to specify whether lines 2 and 3 are chained before -appending line 4, or lines 3 and 4 are chained before prepending line -2. +Because of the associativity law, the programmer does not need to +specify whether lines 2 and 3 are chained before appending line 4, +or lines 3 and 4 are chained before prepending line 2. We will now prove that the Kleisli composition with its laws is equivalent to \lstinline!flatMap! with \emph{its} laws. In other words, we may @@ -5440,7 +5276,7 @@ \subsection{Verifying the monad laws using Kleisli functions} \subsubsection{Statement \label{subsec:Statement-continuation-monad-is-lawful}\ref{subsec:Statement-continuation-monad-is-lawful}} The continuation monad, $\text{Cont}^{R,A}\triangleq\left(A\rightarrow R\right)\rightarrow R$, -satisfies all monad laws. +obeys the monad laws. \subparagraph{Proof} @@ -5503,7 +5339,7 @@ \subsubsection{Statement \label{subsec:Statement-continuation-monad-is-lawful}\r \subsubsection{Statement \label{subsec:Statement-state-monad-is-lawful}\ref{subsec:Statement-state-monad-is-lawful}} The state monad, $\text{State}^{S,A}\triangleq S\rightarrow A\times S$, -satisfies all monad laws. +obeys the monad laws. \subparagraph{Proof} @@ -5545,10 +5381,11 @@ \subsubsection{Statement \label{subsec:Statement-state-monad-is-lawful}\ref{subs \end{align*} These type signatures are complicated and confusing to read. Direct proofs of the monad laws for these functions are much longer than -the proofs of Statements~\ref{subsec:Statement-state-monad-is-lawful}\textendash \ref{subsec:Statement-continuation-monad-is-lawful}. +the proofs of Statements~\ref{subsec:Statement-state-monad-is-lawful}\textendash \ref{subsec:Statement-continuation-monad-is-lawful}. + When a monad $M$ has a function type, the Kleisli function $A\rightarrow M^{B}$ -has two curried arguments. Flipping or uncurrying these arguments -often produces an equivalent function that is easier to work with. +has two curried arguments. Flipping or uncurrying those arguments +often gives an equivalent type that is easier to work with. \subsection{Constructions of semi-monads and monads\label{subsec:Structural-analysis-of-monads}} @@ -5778,7 +5615,7 @@ \subsubsection{Statement \label{subsec:Statement-monad-semimonad-product}\ref{su \end{lstlisting} % \end{minipage}\hfill{} % -\begin{minipage}[t]{0.3\columnwidth}% +\begin{minipage}[t]{0.35\columnwidth}% \begin{lstlisting} val result: (F[C], G[C]) = (result1, result2) @@ -5884,7 +5721,7 @@ \subsubsection{Statement \label{subsec:Statement-co-product-with-identity-monad} denote $\gamma$: \begin{wrapfigure}{l}{0.55\columnwidth}% -\vspace{-0.8\baselineskip} +\vspace{-0.6\baselineskip} \begin{lstlisting} type L[A] = Either[A, F[A]] def gamma[A]: L[A] => F[A] = { @@ -5896,7 +5733,7 @@ \subsubsection{Statement \label{subsec:Statement-co-product-with-identity-monad} \vspace{-2\baselineskip} \end{wrapfigure}% -~\vspace{-0.2\baselineskip} +~\vspace{-0.4\baselineskip} \[ \gamma^{A}\triangleq\,\begin{array}{|c||c|} & F^{A}\\ @@ -5907,10 +5744,7 @@ \subsubsection{Statement \label{subsec:Statement-co-product-with-identity-monad} \noindent Lifting this function to $F$, we can convert $F^{A+F^{A}}$ into $F^{F^{A}}$ and finally into $F^{A}$ via $F$\textsf{'}s \lstinline!flatten! -method: - -\begin{wrapfigure}{l}{0.55\columnwidth}% -\vspace{-0.5\baselineskip} +method \begin{lstlisting} def flatten_L[A]: L[L[A]] => L[A] = { case Left(Left(a)) => Left(a) @@ -5918,11 +5752,6 @@ \subsubsection{Statement \label{subsec:Statement-co-product-with-identity-monad} case Right(g) => Right(g.map(gamma).flatten) } // The last line equals `Right(g.flatMap(gamma))`. \end{lstlisting} - -\vspace{-3\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.1\baselineskip} \[ \text{ftn}_{L}\triangleq\,\begin{array}{|c||cc|} & A & F^{A}\\ @@ -6828,17 +6657,9 @@ \subsubsection{Exercise \label{subsec:Exercise-1-monads-9-1-1}\ref{subsec:Exerci Given a monad $M$, consider a function \lstinline!toUnit! (denoted $\text{tu}_{M}$) with type signature: - -\begin{wrapfigure}{l}{0.61\columnwidth}% -\vspace{-0.5\baselineskip} \begin{lstlisting} def toUnit[M[_]: Monad, A]: M[A] => M[Unit] = _.map(_ => ()) \end{lstlisting} - -\vspace{-0.5\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.9\baselineskip} \[ \text{tu}_{M}:M^{A}\rightarrow M^{\bbnum 1}\triangleq(\_^{:A}\rightarrow1)^{\uparrow M}\quad. \] @@ -6964,17 +6785,9 @@ \subsection{Why monads must be covariant functors} \] The left-hand side equals $\text{flm}_{M}(f\bef\text{pu}_{M})$. We found a formula that expresses \lstinline!map! through \lstinline!flatMap!: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.5\baselineskip} \begin{lstlisting} p.map(f) == p.flatMap(f andThen M.pure) \end{lstlisting} - -\vspace{-0.5\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.1\baselineskip} \begin{equation} f^{\uparrow M}=\text{flm}_{M}(f\bef\text{pu}_{M})\quad.\label{eq:express-map-through-flatMap} \end{equation} @@ -7233,18 +7046,11 @@ \subsubsection{Statement \label{subsec:Statement-tr-equivalent-to-ftr}\ref{subse Functions \lstinline!tr! and \lstinline!ftr! (of the above types) are in a one-to-one correspondence: - -\begin{wrapfigure}{l}{0.3\columnwidth}% -\vspace{-1.9\baselineskip} \[ \xymatrix{\xyScaleY{0.5pc}\xyScaleX{2.8pc} & F^{G^{B}}\ar[rd]\sp(0.5){\ \text{tr}\ }\\ F^{A}\ar[ru]\sp(0.5){(f^{:A\rightarrow G^{B}})^{\uparrow F}~\ }\ar[rr]\sb(0.5){\text{ftr}\,(f^{:A\rightarrow G^{B}})\,} & & K^{B} } \] -\vspace{-0.6\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.6\baselineskip} \begin{equation} \text{ftr}^{A,B}(f)=f^{\uparrow F}\bef\text{tr}^{B}\quad,\quad\quad\text{tr}^{A}=\text{ftr}^{A,A}(\text{id}^{:A\rightarrow A})\quad,\label{eq:define-tr-via-ftr} \end{equation} @@ -7314,6 +7120,9 @@ \subsection{Monads, effects, and runners} \begin{lstlisting} def runner[S, A](init: S): State[S, A] => A = _.run(init)._1 \end{lstlisting} +\begin{equation} +\text{run}_{\text{State}}:S\rightarrow\text{State}^{S,A}\rightarrow A\quad,\quad\quad\text{run}_{\text{State}}(s_{0})\triangleq k^{:S\rightarrow A\times S}\rightarrow s_{0}\triangleright k\triangleright\pi_{1}\quad.\label{eq:definition-of-runState} +\end{equation} The runner for the \lstinline!State! monad computes a final result value of type $A$ by performing all the state updates and other computations encapsulated by a monadic program (\lstinline!s: State[S, A]!). We @@ -7330,27 +7139,17 @@ \subsection{Monads, effects, and runners} first part is a monadic program $m_{1}:M^{A}$, and the second part is a function $m_{2}:A\rightarrow M^{B}$ that depends on the result (of type $A$) of the first monadic program. - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.85\baselineskip} - \begin{lstlisting} val m = for { // m == m1.flatMap(m2) x <- m1 y <- m2(x) } yield y \end{lstlisting} -\vspace{-0.6\baselineskip} -\end{wrapfigure}% - -\noindent The composition of $m_{1}$ and $m_{2}$ can be written -as a functor block or as an application of the \lstinline!flatMap! -method, +The composition of $m_{1}$ and $m_{2}$ can be written as a functor +block or as an application of the \lstinline!flatMap! method, \begin{equation} m=m_{1}\triangleright\text{flm}_{M}(m_{2})\quad.\label{eq:monad-runners-derivation1} \end{equation} -\vspace{-1.2\baselineskip} - We may imagine that $\theta\left(m\right)$ first runs the effects of $m_{1}$ obtaining a value $x$, and then runs the effects of $m_{2}(x)$ obtaining a value $y$. So, it is natural to require that a runner @@ -7359,17 +7158,9 @@ \subsection{Monads, effects, and runners} the runner to $m_{2}(x)$. We can formulate this requirement as a law called the runner\textsf{'}s \index{composition law!of monad runners}\textbf{composition law}: - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.25\baselineskip} - \begin{lstlisting} runner(m) == runner(m2(runner(m1))) \end{lstlisting} -\vspace{-0.2\baselineskip} -\end{wrapfigure}% - -~\vspace{-1\baselineskip} \[ m\triangleright\theta=m_{1}\triangleright\theta\triangleright m_{2}\triangleright\theta\quad. \] @@ -7396,18 +7187,11 @@ \subsection{Monads, effects, and runners} It follows that $\theta\bef m_{2}\bef\theta=m_{2}^{\uparrow M}\bef\theta\bef\theta$ and that $\theta\bef\theta=\theta^{\uparrow M}\bef\theta$. The runner\textsf{'}s composition law becomes: - -\begin{wrapfigure}{l}{0.2\columnwidth}% -\vspace{-1.9\baselineskip} \[ -\xymatrix{\xyScaleY{2.0pc}\xyScaleX{2.3pc}M^{M^{A}}\ar[d]\sb(0.5){\text{ftn}_{M}}\ar[r]\sp(0.5){\theta^{\uparrow M}}\sb(0.5){\theta^{M^{A}}} & M^{A}\ar[d]\sb(0.45){\theta}\\ +\xymatrix{\xyScaleY{2.0pc}\xyScaleX{2.3pc}M^{M^{A}}\ar[d]\sb(0.5){\text{ftn}_{M}}\ar[r]\sp(0.5){\theta^{\uparrow M}} & M^{A}\ar[d]\sb(0.45){\theta}\\ M^{A}\ar[r]\sp(0.5){\theta} & A } \] -\vspace{-0\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.4\baselineskip} \begin{equation} \text{ftn}_{M}\bef\theta=\theta\bef\theta=\theta^{\uparrow M}\bef\theta\quad.\label{eq:runner-composition-law} \end{equation} @@ -7466,10 +7250,6 @@ \subsection{Monads, effects, and runners} if $M$ is a pass/fail monad, we may choose the target monad as $N^{A}=E+A$, where a fixed type $E$ represents error information. We can then define a runner for the \lstinline!Option! monad like this: - -\begin{wrapfigure}{l}{0.43\columnwidth}% -\vspace{-0.45\baselineskip} - \begin{lstlisting} val error: E = ... // Describe the error. def run[A]: Option[A] => Either[E, A] = { @@ -7477,10 +7257,6 @@ \subsection{Monads, effects, and runners} case Some(a) => Right(a) } \end{lstlisting} -\vspace{-2.2\baselineskip} -\end{wrapfigure}% - -~\vspace{-1\baselineskip} \[ \theta^{:\bbnum 1+A\rightarrow E+A}\triangleq\begin{array}{|c||cc|} & E & A\\ @@ -7557,7 +7333,6 @@ \subsubsection{Definition \label{subsec:Definition-monad-morphism}\ref{subsec:De {\color{greenunder}\text{identity law of }\phi:}\quad & \text{pu}_{M}\bef\phi=\text{pu}_{N}\quad,\label{eq:monad-morphism-identity-law}\\ {\color{greenunder}\text{composition law of }\phi:}\quad & \text{ftn}_{M}\bef\phi=\phi^{\uparrow M}\bef\phi\bef\text{ftn}_{N}\quad.\label{eq:monad-morphism-composition-law-using-ftn} \end{align} -\vspace{-1.2\baselineskip} \[ \xymatrix{\xyScaleY{1.2pc}\xyScaleX{4.8pc}A\ar[d]\sb(0.5){\text{pu}_{M}}\ar[rd]\sp(0.5){\text{pu}_{N}} & & M^{M^{A}}\ar[d]\sb(0.5){\phi^{\uparrow M}}\ar[r]\sp(0.5){\text{ftn}_{M}} & M^{A}\ar[rd]\sp(0.5){\phi}\\ M^{A}\ar[r]\sp(0.42){\phi} & N^{A} & M^{N^{A}}\ar[r]\sp(0.5){\phi^{N^{A}}} & N^{N^{A}}\ar[r]\sp(0.5){\text{ftn}_{N}} & N^{A} @@ -7599,25 +7374,17 @@ \subsubsection{Definition \label{subsec:Definition-monad-morphism}\ref{subsec:De and~\ref{chap:monad-transformers}. To build up more intuition, let us look at some examples of monad morphisms. -\subsubsection{Example \label{subsec:Example-monad-morphism-either-option}\ref{subsec:Example-monad-morphism-either-option}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-monad-morphism-either-option}\ref{subsec:Example-monad-morphism-either-option}\index{examples (with code)}} Show that the function $\phi^{A}:Z+A\rightarrow\bbnum 1+A$ defined below is a monad morphism between the \lstinline!Either! and \lstinline!Option! monads. The implementation of $\phi$ is: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.75\baselineskip} - \begin{lstlisting} def toOption[Z, A]: Either[Z, A] => Option[A] = { case Left(z) => None case Right(a) => Some(a) } \end{lstlisting} -\vspace{-1.7\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.7\baselineskip} \[ \phi\triangleq\,\begin{array}{|c||cc|} & \bbnum 1 & A\\ diff --git a/sofp-src/sofp-nameless-functions.lyx b/sofp-src/sofp-nameless-functions.lyx index cb9c7c045..8352224c3 100644 --- a/sofp-src/sofp-nameless-functions.lyx +++ b/sofp-src/sofp-nameless-functions.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -342,7 +357,11 @@ factorial function \series default - of 10). + of 10, usually denoted by +\begin_inset Formula $10!$ +\end_inset + +). \end_layout \begin_layout Subparagraph @@ -350,12 +369,7 @@ Solution \end_layout \begin_layout Standard -First, we write a mathematical formula for the result (usually denoted by - -\begin_inset Formula $10!$ -\end_inset - -): +First, we write a mathematical formula for the result: \begin_inset Formula \[ 10!=1*2*...*10\quad,\quad\quad\text{or in mathematical notation}:\quad10!=\prod_{k=1}^{10}k\quad. @@ -364,21 +378,6 @@ First, we write a mathematical formula for the result (usually denoted by \end_inset We can then write Scala code in a way that resembles the last formula: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "27col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -396,22 +395,9 @@ res0: Int = 3628800 \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\noindent The syntax \begin_inset listings inline true @@ -947,7 +933,33 @@ maps to \begin_inset Quotes erd \end_inset -) like this: +) like this: +\begin_inset Foot +status open + +\begin_layout Plain Layout +In mathematics, the standard symbol for +\begin_inset Quotes eld +\end_inset + +maps to +\begin_inset Quotes erd +\end_inset + + is +\begin_inset Formula $\mapsto$ +\end_inset + +, but this book uses a simpler arrow symbol ( +\begin_inset Formula $\rightarrow$ +\end_inset + +) that is visually similar. +\end_layout + +\end_inset + + \begin_inset Formula \[ x\rightarrow\left(\text{some formula}\right)\quad. @@ -1104,24 +1116,7 @@ status open status open \begin_layout Plain Layout -In mathematics, the -\begin_inset Quotes eld -\end_inset - -maps to -\begin_inset Quotes erd -\end_inset - - symbol is -\begin_inset Formula $\mapsto$ -\end_inset - -, but this book uses a simpler arrow symbol ( -\begin_inset Formula $\rightarrow$ -\end_inset - -) that is visually similar. - Some programming languages use the symbols +Some programming languages use the symbols \begin_inset listings inline true status open @@ -1293,7 +1288,7 @@ fac \end_inset . - An alternative Scala interpreter called + Another Scala interpreter called \begin_inset listings inline true status open @@ -1410,21 +1405,6 @@ fac(10) \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "42col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1442,22 +1422,9 @@ res2: Int = 3628800 \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\noindent We would rarely write code like this. Instead of creating a nameless function and then applying it right away to an argument, it is easier to evaluate the expression symbolically by @@ -1571,21 +1538,6 @@ expression block \series default like this: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1603,22 +1555,9 @@ res3: Int = 322687002 \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\noindent Defined in this way, the value \begin_inset listings inline true @@ -1795,7 +1734,7 @@ This code looks closely similar to the mathematical notation, except for \begin_inset Formula $k$ \end_inset - that introduces a nameless function + that introduces a nameless function ( \begin_inset listings inline true status open @@ -1807,7 +1746,7 @@ k => n % k != 0 \end_inset -. +). We do not need to specify the type \family typewriter @@ -2400,26 +2339,6 @@ forAll \family default with two arguments and write code like this: -\begin_inset space \hfill{} -\end_inset - - -\begin_inset space ~ -\end_inset - - -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2434,15 +2353,7 @@ forAll(2 to n-1, k => n % k != 0) \end_layout -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - +\begin_layout Standard This would bring the syntax closer to Eq. \begin_inset space \space{} \end_inset @@ -2663,26 +2574,6 @@ true \end_inset : -\begin_inset space \hfill{} -\end_inset - - -\begin_inset space ~ -\end_inset - - -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2697,15 +2588,7 @@ status open \end_layout -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - +\begin_layout Standard Just as the mathematical notation defines the variable \begin_inset Formula $k$ \end_inset @@ -2901,21 +2784,9 @@ z => n % z != 0 \end_inset - may be renamed without changing the result of the entire program. - No code outside that function needs to be changed after renaming -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -z -\end_layout - -\end_inset - -. - But the value + is a bound variable: it may be renamed without changing any code outside + that function. + But \begin_inset listings inline true status open @@ -2927,15 +2798,7 @@ n \end_inset - is defined outside and cannot be renamed -\begin_inset Quotes eld -\end_inset - -locally -\begin_inset Quotes erd -\end_inset - - (i.e., only within the sub-expression). + is a free variable (it is not defined inside the function). If we wanted to rename \begin_inset listings inline true @@ -2960,7 +2823,7 @@ z => n % z != 0 \end_inset -, we would also need to change all other code that defines and uses +, we would also need to change all the code that involves the variable \begin_inset listings inline true status open @@ -2976,12 +2839,11 @@ n \emph on outside \emph default - that expression, or else the program would become incorrect. + that sub-expression, or else the program would become incorrect. \end_layout \begin_layout Standard -Mathematical formulas use bound variables in various constructions such - as +Mathematical formulas use bound variables in constructions such as \begin_inset Formula $\forall k.\,p(k)$ \end_inset @@ -3007,8 +2869,7 @@ Mathematical formulas use bound variables in various constructions such . When translating mathematical expressions into code, we need to recognize - the presence of bound variables, which the mathematical notation does not - make quite so explicit. + the bound variables present in the mathematical notation. For each bound variable, we create a nameless function whose argument is that variable, e.g., \begin_inset listings @@ -3017,7 +2878,7 @@ status open \begin_layout Plain Layout -k=>p(k) +k => p(k) \end_layout \end_inset @@ -3029,7 +2890,7 @@ status open \begin_layout Plain Layout -k=>f(k) +k => f(k) \end_layout \end_inset @@ -3130,7 +2991,7 @@ f(x) \end_inset - but actually uses an automatic type conversion for the argument + but actually uses an automatic conversion for the argument \begin_inset listings inline true status open @@ -3578,7 +3439,7 @@ status open \begin_layout Plain Layout -scala> List(1, 2, 3).map(x => x*x + 100*x) +scala> List(1, 2, 3).map(x => x * x + 100 * x) \end_layout \begin_layout Plain Layout @@ -3625,7 +3486,7 @@ map \family default - to transform each integer from + to transform each integer from the sequence \begin_inset listings inline true status open @@ -3745,7 +3606,7 @@ res3: List[Int] = List(101, 204, 309) \end_layout \begin_layout Standard -If the transforming function +If the transforming function is used only once (such as \begin_inset listings inline true status open @@ -3757,11 +3618,11 @@ func1 \end_inset - is used only once, and especially for a simple computation such as -\begin_inset Formula $x\rightarrow x*x+100*x$ + in the example above), and especially for simple computations such as +\begin_inset Formula $x\rightarrow x^{2}+100x$ \end_inset -, it is easier to work with a nameless function. +, it is easier to use a nameless function. \end_layout \begin_layout Standard @@ -3922,36 +3783,35 @@ sum \begin_layout Standard To test this code, let us run it in the Scala interpreter. - In order to let the interpreter work correctly with code entered line by - line, the dot character needs to be at the -\emph on -end -\emph default - of the line. - (In compiled code, the dots may be at the beginning of line since the compiler - reads the entire file at once.) + In order to let the interpreter work correctly with multi-line code, we + will enclose the code in braces: \begin_inset listings inline false status open \begin_layout Plain Layout -scala> def countEven(s: List[Int]): Int = s. +scala> def countEven(s: List[Int]): Int = { +\end_layout + +\begin_layout Plain Layout + + | s.map { k => if (k % 2 == 0) 1 else 0 } \end_layout \begin_layout Plain Layout - map { k => if (k % 2 == 0) 1 else 0 }. + | .sum \end_layout \begin_layout Plain Layout - sum + | } \end_layout \begin_layout Plain Layout -countEven: (s: List[Int])Int +def countEven: (s: List[Int])Int \end_layout \begin_layout Plain Layout @@ -4855,12 +4715,12 @@ reduce \end_layout \begin_layout Section -Solved examples +Examples \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -5958,23 +5818,7 @@ filter \end_inset . - The inner expression -\end_layout - -\begin_layout Standard -\noindent -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - + The inner expression: \begin_inset listings inline false status open @@ -5986,19 +5830,7 @@ status open \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - (shown at left) computes the list of +computes a list of all \begin_inset Formula $j$ \end_inset @@ -6006,7 +5838,8 @@ status open \begin_inset Formula $j*j>2*k$ \end_inset -, and then compares the size of that list with +. + The size of that list is then compared with \begin_inset Formula $3$ \end_inset @@ -6507,14 +6340,13 @@ Example \begin_inset space ~ \end_inset -2: Compute a floating-point number from a list of characters showing the - decimal representation of that number. +2: Compute a list of partial sums from a given list of integers. For example, the list -\begin_inset Formula $\left["1","2",".","3"\right]$ +\begin_inset Formula $\left[1,2,3,4\right]$ \end_inset - should be transformed into the number -\begin_inset Formula $12.3$ + should be transformed into +\begin_inset Formula $\left[1,3,6,10\right]$ \end_inset . @@ -6674,41 +6506,26 @@ takeWhile \end_layout \begin_layout Standard -Example +An implementation of Example \begin_inset space \space{} \end_inset -2 corresponds to the function -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -digitsToDouble -\end_layout - -\end_inset - - (Example +2 is shown in Section \begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Example-4-digitstodouble-foldleft" +reference "sec:Transforming-a-sequence" plural "false" caps "false" noprefix "false" \end_inset -). - The input is processed one character at a time. - At each step, the floating-point result is updated depending on the previous - result and on whether a decimal point has already been seen. - Operations such as +. + This cannot be implemented with operations such as \family typewriter \begin_inset listings @@ -6740,7 +6557,8 @@ filter \family default - cannot compute the next element of a sequence depending on previous values + because they cannot produce sequences whose next elements depend on previous + values. \end_layout \begin_layout Standard @@ -7055,8 +6873,8 @@ status open \family typewriter \begin_inset CommandInset href LatexCommand href -name "https://en.wikipedia.org/wiki/Proof_of_the_Euler_product_formula_for_the_Riemann_zeta_function" -target "https://en.wikipedia.org/wiki/Proof_of_the_Euler_product_formula_for_the_Riemann_zeta_function" +name "http://tinyurl.com/4rjj2rvc" +target "http://tinyurl.com/4rjj2rvc" literal "false" \end_inset @@ -7066,7 +6884,25 @@ literal "false" \end_inset - for the Riemann's zeta function + +\begin_inset Note Note +status collapsed + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://en.wikipedia.org/wiki/Proof_of_the_Euler_product_formula_for_the_Riemann_zeta_function" + +\end_inset + + +\end_layout + +\end_inset + +for the Riemann's zeta function \begin_inset Index idx status open @@ -7108,7 +6944,7 @@ literal "false" : \begin_inset Formula \[ -\zeta\left(4\right)=\prod_{k\geq2;~k\text{ is prime}}\frac{1}{1-\frac{1}{p^{4}}}=\frac{\pi^{4}}{90}\quad. +\zeta\left(4\right)=\prod_{k\geq2;~k\text{ is prime}}\frac{1}{1-\frac{1}{k^{4}}}=\frac{\pi^{4}}{90}\quad. \] \end_inset @@ -7238,7 +7074,15 @@ An integer \end_inset if it is divisible by only three different integers -\begin_inset Formula $i,j,k$ +\begin_inset Formula $i$ +\end_inset + +, +\begin_inset Formula $j$ +\end_inset + +, +\begin_inset Formula $k$ \end_inset such that @@ -7324,14 +7168,22 @@ f: Int => Boolean \end_inset if there are only three different integers -\begin_inset Formula $j\in[1,...,n]$ +\begin_inset Formula $1 see100( List( List(0, 1, 100), List(60, 80), List(1000) ) ) +scala> at100( List( List(0, 1, 100), List(60, 80), List(1000) ) ) \end_layout \begin_layout Plain Layout @@ -7499,17 +7351,17 @@ List[Double] => List[Double] \family default - that + that performs a \begin_inset Quotes eld \end_inset -normalizes +normalization \begin_inset Quotes erd \end_inset - the list: it finds the element having the largest absolute value and, if - that value is nonzero, divides all elements by that value and returns a - new list; otherwise returns the original list. + of a list: it finds the element having the largest absolute value and, + if that value is zero, returns the original list; if that value is nonzero, + divides all elements by that value and returns a new list. Test with: \begin_inset listings inline false @@ -7517,12 +7369,12 @@ status open \begin_layout Plain Layout -scala> normalize(List(1.0, 4.0, 2.0)) +scala> normalize(List(1.0, -4.0, 2.0)) \end_layout \begin_layout Plain Layout -res0: List[Double] = List(0.25, 1.0, 0.5) +res0: List[Double] = List(0.25, -1.0, 0.5) \end_layout \end_inset @@ -7573,10 +7425,10 @@ The main idea of FP is to write code as a mathematical expression or formula \emph default . - This approach allows programmers to derive code through logical reasoning - rather than through guessing, similarly to how books on mathematics reason - about mathematical formulas and derive results systematically, without - guessing or + This allows programmers to derive code through logical reasoning rather + than through guessing, similarly to how books on mathematics reason about + mathematical formulas and derive results systematically, without guessing + or \begin_inset Quotes eld \end_inset @@ -7611,8 +7463,8 @@ As we have seen, the Scala code for certain computational tasks corresponds write out some details that are omitted in the mathematical notation). Just as in mathematics, large code expressions may be split into smaller expressions when needed. - Expressions can be easily reused, composed in various ways, and written - independently from each other. + Expressions can be reused, composed in various ways, and written independently + from each other. Over the years, the FP community has developed a toolkit of functions (such as \begin_inset listings @@ -7642,21 +7494,33 @@ filter \family default -, etc.) that proved to be especially useful in real-life programming, although - many of them are not standard in mathematical literature. -\end_layout - +, +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +flatMap +\end_layout + +\end_inset + +, etc.), which are not standard in mathematical literature but proved to + be useful in practical programming. +\end_layout + \begin_layout Standard -Mastering FP involves practicing to reason about programs as formulas +Mastering FP involves practicing to write programs as \begin_inset Quotes eld \end_inset -translated into code +formulas translated into code \begin_inset Quotes erd \end_inset , building up the specific kind of applied mathematical intuition, and getting - familiar with mathematical concepts adapted to a programmer's needs. + familiar with certain mathematical concepts adapted to a programmer's needs. The FP community has discovered a number of specific programming idioms founded on mathematical principles but driven by practical necessities of writing software. @@ -7667,9 +7531,9 @@ translated into code \begin_layout Standard This chapter explored the first significant idiom of FP: iterative calculations - performed without loops, in the style of mathematical expressions. - This technique can be easily used in any programming language that supports - nameless functions. + performed without loops in the style of mathematical expressions. + This technique can be used in any programming language that supports nameless + functions. \end_layout @@ -8249,13 +8113,16 @@ Iteration without loops \end_layout \begin_layout Standard -A distinctive feature of the FP paradigm is handling of iteration without - writing loops. +A distinctive feature of the FP paradigm is handling of iteration +\emph on +without +\emph default + writing loops, just as iterative computations are written in mathematical + notation. \end_layout \begin_layout Standard -Iterative computations are ubiquitous in mathematics. - As an example, consider the formula for the standard deviation ( +As an example, consider the formula for the standard deviation ( \begin_inset Formula $\sigma$ \end_inset @@ -8578,24 +8445,41 @@ integration(0, x, { z => 1.0 / (1 + z) } ) \end_inset -Now consider the traditional mathematical notation for summation, for instance: + +\end_layout + +\begin_layout Standard +Now compare the mathematical notations for integration and for summation: + +\begin_inset Formula $\int_{0}^{x}\frac{dz}{1+z}$ +\end_inset + + and +\begin_inset Formula $\sum_{k=0}^{100}\frac{1}{1+k}$ +\end_inset + +. +\begin_inset Note Note +status collapsed + +\begin_layout Plain Layout \begin_inset Formula \[ -\sum_{k=0}^{x}\frac{1}{1+k}\quad. +{\displaystyle \int}_{0}^{x}\frac{dz}{1+z}\quad,\quad\quad\sum_{k=0}^{x}\frac{1}{1+k}\quad. \] \end_inset -In that sum, the bound variable -\begin_inset Formula $k$ + +\end_layout + \end_inset - is introduced under the -\begin_inset Formula $\sum$ + The integral defines a bound variable +\begin_inset Formula $z$ \end_inset - symbol; but in integrals, the bound variable follows the special symbol - + via the special symbol \begin_inset Quotes eld \end_inset @@ -8607,9 +8491,17 @@ In that sum, the bound variable \begin_inset Quotes erd \end_inset +, while the summation places a bound variable +\begin_inset Formula $k$ +\end_inset + + in a subscript under +\begin_inset Formula $\sum$ +\end_inset + . - This notational inconsistency could be removed if we were to use nameless - functions explicitly, for example: + The notation could be made more consistent by using nameless functions + explicitly, for example like this: \begin_inset Formula \begin{align*} \text{denote summation by }\sum_{0}^{x} & \left(k\rightarrow\frac{1}{1+k}\right)\text{ instead of }\sum_{k=0}^{x}\frac{1}{1+k}\quad,\\ @@ -8618,7 +8510,7 @@ In that sum, the bound variable \end_inset -In this notation, the new summation symbol +In the new notation, the summation symbol \begin_inset Formula $\sum_{0}^{x}$ \end_inset @@ -8635,7 +8527,7 @@ In this notation, the new summation symbol \end_inset but takes a function as an argument. - Similarly, the new integration symbol + Similarly, the integration symbol \begin_inset Formula $\int_{0}^{x}$ \end_inset @@ -8663,7 +8555,7 @@ In this notation, the new summation symbol \begin_inset Quotes erd \end_inset - but now takes a function as an argument. + but takes a function as an argument. Written in this way, the operations of summation and integration become \emph on @@ -8763,26 +8655,26 @@ literal "false" \end_inset - is an algorithm for approximate numerical integration, defined by the formulas: + gives the following formulas for numerical integration: \begin_inset Formula \begin{align*} -\text{integration}\left(a,b,g,\varepsilon\right) & =\frac{\delta}{3}\big(g(a)+g(b)+4s_{1}+2s_{2}\big)\quad,\\ -\text{where }~~~n & =2\left\lfloor \frac{b-a}{\varepsilon}\right\rfloor ,\quad\delta_{x}=\frac{b-a}{n}\quad,\\ +\text{Simpson}\left(a,b,g,\varepsilon\right) & =\frac{\delta}{3}\big(g(a)+g(b)+4s_{1}+2s_{2}\big)\quad,\\ +\text{where }~~~n & =2\left\lfloor \frac{b-a}{\varepsilon}\right\rfloor \quad,\quad\quad\delta_{x}=\frac{b-a}{n}\quad,\\ s_{1}=\sum_{k=1,3,...,n-1}g(a+k\delta_{x}) & \quad,\quad\quad s_{2}=\sum_{k=2,4,...,n-2}g(a+k\delta_{x})\quad. \end{align*} \end_inset - Here is a straightforward line-by-line translation of these formulas into - Scala, with some tests: +Here is a straightforward line-by-line translation of these formulas into + Scala: \begin_inset listings inline false status open \begin_layout Plain Layout -def integration(a: Double, b: Double, g: Double => Double, eps: Double): - Double = { +def simpson(a: Double, b: Double, g: Double => Double, eps: Double): Double + = { \end_layout \begin_layout Plain Layout @@ -8844,8 +8736,7 @@ where n = ... \begin_layout Plain Layout -scala> integration(0, 5, x => x*x*x*x, eps = 0.01) // The exact answer - is 625. +scala> simpson(0, 5, x => x*x*x*x, eps = 0.01) // The answer is 625. \end_layout \begin_layout Plain Layout @@ -8859,8 +8750,7 @@ res0: Double = 625.0000000004167 \begin_layout Plain Layout -scala> integration(0, 7, x => x*x*x*x*x*x, eps = 0.01) // The exact answer - is 117649. +scala> simpson(0, 7, x => x*x*x*x*x*x, eps = 0.01) // The answer is 117649. \end_layout \begin_layout Plain Layout @@ -8955,7 +8845,7 @@ status open \begin_layout Plain Layout -(x+123)*y/(4+x) +(x + 123) * y / (4 + x) \end_layout \end_inset @@ -8973,7 +8863,7 @@ status open \begin_layout Plain Layout -x+123 +x + 123 \end_layout \end_inset @@ -8987,7 +8877,7 @@ status open \begin_layout Plain Layout -4+x +4 + x \end_layout \end_inset @@ -9005,12 +8895,12 @@ status open \begin_layout Plain Layout -(x+123)*y/(4+x) +(x + 123) * y / (4 + x) \end_layout \end_inset - would then look like this: + would look like this: \end_layout \begin_layout Standard @@ -9079,8 +8969,8 @@ assembly language \end_inset -, where every sub-expression — that is, every step of every calculation - — must be assigned a separate memory address or a CPU register. +: every sub-expression — that is, every step of every calculation — needs + to be written into a separate memory address or a CPU register. \end_layout \begin_layout Standard @@ -9091,7 +8981,7 @@ Programmers become more productive when their programming language supports \end_layout \begin_layout Standard -It is also useful if data structures can be created without names. +It is also useful to be able to create nameless data structures. For instance, a \series bold dictionary @@ -9122,7 +9012,7 @@ hashmap \begin_inset Quotes erd \end_inset -) may be created in Scala with this code: +) is created in Scala with this code: \begin_inset listings inline false status open @@ -9137,7 +9027,7 @@ Map("a" -> 1, "b" -> 2, "c" -> 3) This is a nameless expression whose value is a dictionary. In programming languages that do not have such a construction, programmers have to write special code that creates an initially empty dictionary and - then fills it step by step with values: + then fills in one value at a time: \begin_inset listings lstparams "language=Java" inline false @@ -9211,21 +9101,15 @@ Historical perspective on nameless functions \end_layout \begin_layout Standard -\begin_inset Wrap table -lines 0 -placement r -overhang 0in -width "60col%" +\begin_inset Float table +wide false +sideways false status open \begin_layout Plain Layout \align center -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset Tabular - + @@ -9395,6 +9279,53 @@ status open \end_inset +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout + +\size footnotesize +ALGOL 68 +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\size footnotesize +1968 +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\family typewriter +\size footnotesize +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +(INT k) INT: k + 1 +\end_layout + +\end_inset + + \end_layout \end_inset @@ -9486,6 +9417,50 @@ fun (k: int) -> k + 1 \end_inset +\end_layout + +\end_inset + + + + +\begin_inset Text + +\begin_layout Plain Layout + +\size footnotesize +Erlang +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout + +\size footnotesize +1986 +\end_layout + +\end_inset + + +\begin_inset Text + +\begin_layout Plain Layout +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fun(K) -> K + 1 end +\end_layout + +\end_inset + + \end_layout \end_inset @@ -10230,7 +10205,7 @@ status open \begin_layout Plain Layout -{ (k:int) -> int in return k + 1 } +{ (k: int) -> int in return k + 1 } \end_layout \end_inset @@ -10340,7 +10315,7 @@ status open \begin_inset Caption Standard \begin_layout Plain Layout -Nameless functions in programming languages. +Nameless functions in various programming languages. \begin_inset CommandInset label LatexCommand label name "lambda-functions-table" @@ -10353,17 +10328,73 @@ name "lambda-functions-table" \end_inset -\begin_inset VSpace -300baselineskip% +\end_layout + +\begin_layout Plain Layout + +\end_layout + \end_inset \end_layout +\begin_layout Standard +What this book calls (for clarity) a +\begin_inset Quotes eld +\end_inset + +nameless function +\begin_inset Quotes erd +\end_inset + + is also known as an anonymous function, +\begin_inset Index idx +status open + +\begin_layout Plain Layout +anonymous function!see +\begin_inset Quotes eld +\end_inset + +nameless functions +\begin_inset Quotes erd \end_inset \end_layout +\end_inset + + a function expression, a function literal, a closure, a +\begin_inset Index idx +status open + +\begin_layout Plain Layout +lambda-function!see +\begin_inset Quotes eld +\end_inset + +nameless function +\begin_inset Quotes erd +\end_inset + + +\end_layout + +\end_inset + +lambda function, a lambda expression, or just a +\begin_inset Quotes eld +\end_inset + +lambda +\begin_inset Quotes erd +\end_inset + +. +\end_layout + \begin_layout Standard Nameless functions were first used in 1936 in a theoretical programming language called @@ -10415,14 +10446,24 @@ programming language \emph default , not related to differential or integral calculus. - Also, the symbol + +\begin_inset Note Note +status collapsed + +\begin_layout Plain Layout +Also, the symbol \begin_inset Formula $\lambda$ \end_inset has no particular significance other than a syntactic separator used in functions. - Practitioners of functional programming usually do not need to study any +\end_layout + +\end_inset + +Practitioners of functional programming do not need to study the theory + of \begin_inset Formula $\lambda$ \end_inset @@ -10455,20 +10496,12 @@ noprefix "false" \begin_inset Formula $\lambda$ \end_inset - (pronounced as -\begin_inset Quotes eld -\end_inset - -lambda -\begin_inset Quotes erd -\end_inset - -) is a syntax separator that denotes function arguments in nameless functions. + is a syntax separator that denotes function arguments in nameless functions. For example, the nameless function \begin_inset Formula $x\rightarrow x+1$ \end_inset - could be written as + would be written as \begin_inset Formula $\lambda x.~add~x~1$ \end_inset @@ -10544,70 +10577,5 @@ reference "lambda-functions-table" shows the year when various languages supported nameless functions. \end_layout -\begin_layout Standard -What this book calls a -\begin_inset Quotes eld -\end_inset - -nameless function -\begin_inset Quotes erd -\end_inset - - is also known as an anonymous function, -\begin_inset Index idx -status open - -\begin_layout Plain Layout -anonymous function!see -\begin_inset Quotes eld -\end_inset - -nameless functions -\begin_inset Quotes erd -\end_inset - - -\end_layout - -\end_inset - - a function expression, a function literal, a closure, a -\begin_inset Index idx -status open - -\begin_layout Plain Layout -lambda-function!see -\begin_inset Quotes eld -\end_inset - -nameless function -\begin_inset Quotes erd -\end_inset - - -\end_layout - -\end_inset - -lambda function, a lambda expression, or just a -\begin_inset Quotes eld -\end_inset - -lambda -\begin_inset Quotes erd -\end_inset - -. - This book prefers the clearer term -\begin_inset Quotes eld -\end_inset - -nameless function -\begin_inset Quotes erd -\end_inset - -. -\end_layout - \end_body \end_document diff --git a/sofp-src/sofp-nameless-functions.tex b/sofp-src/sofp-nameless-functions.tex index e8e0818e6..c04045036 100644 --- a/sofp-src/sofp-nameless-functions.tex +++ b/sofp-src/sofp-nameless-functions.tex @@ -11,30 +11,23 @@ \subsubsection{Example \label{subsec:Example-Factorial-of-10}\ref{subsec:Example Factorial of 10} Find the product of integers from $1$ to $10$ (the \textbf{factorial\index{factorial function}} -of 10). +of 10, usually denoted by $10!$). \subparagraph{Solution} -First, we write a mathematical formula for the result (usually denoted -by $10!$): +First, we write a mathematical formula for the result: \[ 10!=1*2*...*10\quad,\quad\quad\text{or in mathematical notation}:\quad10!=\prod_{k=1}^{10}k\quad. \] We can then write Scala code in a way that resembles the last formula: - -\begin{wrapfigure}{l}{0.27\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} scala> (1 to 10).product res0: Int = 3628800 \end{lstlisting} -\vspace{-0.8\baselineskip} -\end{wrapfigure}% - -\noindent The syntax \lstinline!(1 to 10)! produces a sequence of -integers from \lstinline!1! to \lstinline!10!. The \lstinline!product! -method computes the product of the numbers in the sequence. +The syntax \lstinline!(1 to 10)! produces a sequence of integers +from \lstinline!1! to \lstinline!10!. The \lstinline!product! method +computes the product of the numbers in the sequence. The code \texttt{}\lstinline!(1 to 10).product! is an \textbf{expression}\index{expression}, which means that (1) the code can be evaluated and yields a value, @@ -107,7 +100,9 @@ \subsection{Nameless functions\label{subsec:Nameless-functions}} involve \emph{naming} the function as \textsf{``}$f$\textsf{''}. Sometimes a function does not really need a name, \textemdash{} say, if the function is used only once. \textsf{``}Nameless\textsf{''} mathematical functions may be denoted -using the symbol $\rightarrow$ (pronounced \textsf{``}maps to\textsf{''}) like this: +using the symbol $\rightarrow$ (pronounced \textsf{``}maps to\textsf{''}) like this:\footnote{In mathematics, the standard symbol for \textsf{``}maps to\textsf{''} is $\mapsto$, +but this book uses a simpler arrow symbol ($\rightarrow$) that is +visually similar.} \[ x\rightarrow\left(\text{some formula}\right)\quad. \] @@ -128,9 +123,7 @@ \subsection{Nameless functions\label{subsec:Nameless-functions}} is actually applied. This book uses the word \textsf{``}argument\textsf{''} for both, following the mathematical usage.} while \lstinline!(1 to n).product! is the function\textsf{'}s \textbf{body}. The function arrow (\lstinline!=>!) separates the argument variable -from the body.\footnote{In mathematics, the \textsf{``}maps to\textsf{''} symbol is $\mapsto$, but this book -uses a simpler arrow symbol ($\rightarrow$) that is visually similar. -Some programming languages use the symbols \lstinline!->! or \lstinline!=>! +from the body.\footnote{Some programming languages use the symbols \lstinline!->! or \lstinline!=>! for the function arrow; see Table~\ref{lambda-functions-table}.} Functions in Scala (whether named or nameless) are treated as values\index{function as a value}, @@ -144,8 +137,8 @@ \subsection{Nameless functions\label{subsec:Nameless-functions}} argument and returns an integer result value. What is the value of the function \lstinline!fac! \emph{itself}? As we have just seen, the standard Scala interpreter prints \lstinline!! as -the \textsf{``}value\textsf{''} of \lstinline!fac!. An alternative Scala interpreter -called \lstinline!ammonite!\footnote{See \texttt{\href{https://ammonite.io/}{https://ammonite.io/}}} +the \textsf{``}value\textsf{''} of \lstinline!fac!. Another Scala interpreter called +\lstinline!ammonite!\footnote{See \texttt{\href{https://ammonite.io/}{https://ammonite.io/}}} prints this: \begin{lstlisting} scala@ val fac = (n: Int) => (1 to n).product @@ -166,21 +159,15 @@ \subsection{Nameless functions\label{subsec:Nameless-functions}} However, functions can be used without naming them. We may directly apply a nameless factorial function to an integer argument \lstinline!10! instead of writing \lstinline!fac(10)!: - -\begin{wrapfigure}{l}{0.42\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} scala> ((n: Int) => (1 to n).product)(10) res2: Int = 3628800 \end{lstlisting} -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent We would rarely write code like this. Instead of creating -a nameless function and then applying it right away to an argument, -it is easier to evaluate the expression symbolically by substituting -\lstinline!10! instead of \lstinline!n! in the function body: +We would rarely write code like this. Instead of creating a nameless +function and then applying it right away to an argument, it is easier +to evaluate the expression symbolically by substituting \lstinline!10! +instead of \lstinline!n! in the function body: \begin{lstlisting} ((n: Int) => (1 to n).product)(10) == (1 to 10).product \end{lstlisting} @@ -198,21 +185,15 @@ \subsection{Nameless functions\label{subsec:Nameless-functions}} but, of course, it is better to avoid repeating the value \lstinline!12345!. To achieve that, we may define \texttt{}\lstinline!n! as a value in an \textbf{expression block\index{expression block}} like this: - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} scala> { val n = 12345; n*n*n + n*n } res3: Int = 322687002 \end{lstlisting} -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent Defined in this way, the value \lstinline!n! is visible -only within the expression block. Outside the block, another value -named \lstinline!n! could be defined independently of this \lstinline!n!. -For this reason, the definition of \lstinline!n! is called a \textbf{local-scope}\index{local scope} +Defined in this way, the value \lstinline!n! is visible only within +the expression block. Outside the block, another value named \lstinline!n! +could be defined independently of this \lstinline!n!. For this reason, +the definition of \lstinline!n! is called a \textbf{local-scope}\index{local scope} definition. Nameless functions are convenient when they are themselves arguments @@ -237,7 +218,7 @@ \subsubsection{Example \label{subsec:Example-prime-numbers}\ref{subsec:Example-p def isPrime(n: Int) = (2 to n-1).forall(k => n % k != 0) \end{lstlisting} This code looks closely similar to the mathematical notation, except -for the arrow after $k$ that introduces a nameless function \lstinline*k => n % k != 0*. +for the arrow after $k$ that introduces a nameless function (\lstinline*k => n % k != 0*). We do not need to specify the type \texttt{}\lstinline!Int! for the argument \texttt{}\lstinline!k! of that nameless function. The Scala compiler knows that \texttt{}\lstinline!k! is going to iterate @@ -299,14 +280,11 @@ \subsection{Nameless functions and bound variables} are already provided in the Scala standard library, so it is natural to use them. If we want to avoid the method syntax, we could define a function \texttt{}\lstinline!forAll! with two arguments and write -code like this:\hfill{}~ \begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.8\baselineskip} +code like this: \begin{lstlisting} forAll(2 to n-1, k => n % k != 0) \end{lstlisting} -\vspace{-1.5\baselineskip} -\end{wrapfigure}% This would bring the syntax closer to Eq.\ (\ref{eq:is_prime_def}). However, there still remains the second difference: The symbol $k$ is used as an \emph{argument} of a nameless function \lstinline*(k => n % k != 0)* @@ -335,15 +313,11 @@ \subsection{Nameless functions and bound variables} the mathematical notation into code, it is therefore natural to use the nameless function $k\rightarrow(n\%k)\neq0$ and to write Scala code applying this nameless function to each element of the range -$\left[2,n-1\right]$ and checking that all result values be \lstinline!true!:\hfill{}~ -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.8\baselineskip} +$\left[2,n-1\right]$ and checking that all result values be \lstinline!true!: \begin{lstlisting} (2 to n-1).forall(k => n % k != 0) \end{lstlisting} -\vspace{-1.5\baselineskip} -\end{wrapfigure}% Just as the mathematical notation defines the variable $k$ only in the right-hand side of Eq.\ (\ref{eq:is_prime_def}), the argument \lstinline!k! of the nameless Scala function \lstinline*k => n % k != 0* @@ -373,25 +347,22 @@ \subsection{Nameless functions and bound variables} \end{lstlisting} The argument \lstinline!z! in the nameless function \lstinline*z => n % z != 0* -may be renamed without changing the result of the entire program. -No code outside that function needs to be changed after renaming \lstinline!z!. -But the value \lstinline!n! is defined outside and cannot be renamed -\textsf{``}locally\textsf{''} (i.e., only within the sub-expression). If we wanted -to rename \lstinline!n! in the sub-expression \lstinline*z => n % z != 0*, -we would also need to change all other code that defines and uses -\lstinline!n! \emph{outside} that expression, or else the program -would become incorrect. - -Mathematical formulas use bound variables in various constructions -such as $\forall k.\,p(k)$, $\exists k.\,p(k)$, $\sum_{k=a}^{b}f(k)$, -$\int_{0}^{1}k^{2}dk$, $\lim_{n\rightarrow\infty}f(n)$, and $\text{argmax}_{k}f\left(k\right)$. +is a bound variable: it may be renamed without changing any code outside +that function. But \lstinline!n! is a free variable (it is not defined +inside the function). If we wanted to rename \lstinline!n! in the +sub-expression \lstinline*z => n % z != 0*, we would also need to +change all the code that involves the variable \lstinline!n! \emph{outside} +that sub-expression, or else the program would become incorrect. + +Mathematical formulas use bound variables in constructions such as +$\forall k.\,p(k)$, $\exists k.\,p(k)$, $\sum_{k=a}^{b}f(k)$, $\int_{0}^{1}k^{2}dk$, +$\lim_{n\rightarrow\infty}f(n)$, and $\text{argmax}_{k}f\left(k\right)$. When translating mathematical expressions into code, we need to recognize -the presence of bound variables, which the mathematical notation does -not make quite so explicit. For each bound variable, we create a nameless -function whose argument is that variable, e.g., \lstinline!k=>p(k)! -or \lstinline!k=>f(k)! for the examples just shown. Then our code -will correctly reproduce the behavior of bound variables in mathematical -expressions. +the bound variables present in the mathematical notation. For each +bound variable, we create a nameless function whose argument is that +variable, e.g., \lstinline!k => p(k)! or \lstinline!k => f(k)! for +the examples just shown. Then our code will correctly reproduce the +behavior of bound variables in mathematical expressions. As an example, the mathematical formula $\forall k\in\left[1,n\right].\,p\left(k\right)$ has a bound variable $k$ and is translated into Scala code as: @@ -411,9 +382,9 @@ \subsection{Nameless functions and bound variables} The simplification of $x\rightarrow f(x)$ to just $f$ is always possible for functions $f$ of a single argument.\footnote{Certain features of Scala allow programmers to write code that looks -like \lstinline!f(x)! but actually uses an automatic type conversion -for the argument \lstinline!x! or additional hidden arguments of -the function \lstinline!f!. In those cases, replacing the code \lstinline!x => f(x)! +like \lstinline!f(x)! but actually uses an automatic conversion for +the argument \lstinline!x! or additional hidden arguments of the +function \lstinline!f!. In those cases, replacing the code \lstinline!x => f(x)! by \lstinline!f! will fail to compile. This problem does not appear when working with simple functions.} @@ -483,14 +454,14 @@ \section{Aggregating data from sequences} in a \emph{new }list, which is then returned as the result value: \begin{lstlisting} -scala> List(1, 2, 3).map(x => x*x + 100*x) +scala> List(1, 2, 3).map(x => x * x + 100 * x) res1: List[Int] = List(101, 204, 309) \end{lstlisting} In this example, the argument of \lstinline!map! is the nameless function $x\rightarrow x^{2}+100x$. This function will be used repeatedly -by \texttt{}\lstinline!map! to transform each integer from \lstinline!List(1, 2, 3)!, -creating a new list as a result. +by \texttt{}\lstinline!map! to transform each integer from the sequence +\lstinline!List(1, 2, 3)!, creating a new list as a result. It is equally possible to define the transforming function separately, give it a name, and then use it as the argument to \lstinline!map!: @@ -511,9 +482,9 @@ \section{Aggregating data from sequences} res3: List[Int] = List(101, 204, 309) \end{lstlisting} -If the transforming function \lstinline!func1! is used only once, -and especially for a simple computation such as $x\rightarrow x*x+100*x$, -it is easier to work with a nameless function. +If the transforming function is used only once (such as \lstinline!func1! +in the example above), and especially for simple computations such +as $x\rightarrow x^{2}+100x$, it is easier to use a nameless function. We can now combine the methods \texttt{}\lstinline!map! and \texttt{}\lstinline!sum! to define \lstinline!countEven!: @@ -538,15 +509,14 @@ \section{Aggregating data from sequences} of the chained methods on a new line. To test this code, let us run it in the Scala interpreter. In order -to let the interpreter work correctly with code entered line by line, -the dot character needs to be at the \emph{end} of the line. (In compiled -code, the dots may be at the beginning of line since the compiler -reads the entire file at once.) +to let the interpreter work correctly with multi-line code, we will +enclose the code in braces: \begin{lstlisting} -scala> def countEven(s: List[Int]): Int = s. - map { k => if (k % 2 == 0) 1 else 0 }. - sum -countEven: (s: List[Int])Int +scala> def countEven(s: List[Int]): Int = { + | s.map { k => if (k % 2 == 0) 1 else 0 } + | .sum + | } +def countEven: (s: List[Int])Int scala> countEven(List(1,2,3,4,5)) res0: Int = 2 @@ -640,7 +610,7 @@ \section{Filtering and truncating a sequence } and aggregation is known as programming in the \textbf{map/reduce} \textbf{style}.\index{map/reduce programming style@\texttt{map}/\texttt{reduce} programming style|textit} -\section{Solved examples\index{solved examples}} +\section{Examples\index{examples (with code)}} \subsection{Aggregations\label{subsec:Aggregation-solved-examples}} @@ -658,10 +628,11 @@ \subsubsection{Example \label{subsec:ch1-aggr-Example-1}\ref{subsec:ch1-aggr-Exa Use \lstinline!takeWhile! to truncate the initial list when $k*k\leq n$ becomes false: \begin{lstlisting} -def isPrime(n: Int): Boolean = +def isPrime(n: Int): Boolean = { (2 to n-1) .takeWhile(k => k*k <= n) .forall(k => n % k != 0) +} \end{lstlisting} @@ -835,21 +806,15 @@ \subsubsection{Example \label{subsec:ch1-Example-2}\ref{subsec:ch1-Example-2}} res0: List[Int] = List(6, 7, 8, 9, 10) \end{lstlisting} The argument of the outer \lstinline!filter! is a nameless function -that also uses a \lstinline!filter!. The inner expression - -\noindent \begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0.8\baselineskip} +that also uses a \lstinline!filter!. The inner expression: \begin{lstlisting} (1 to k).filter(j => j*j > 2*k).size >= 3 \end{lstlisting} - -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - (shown at left) computes the list of $j$\textsf{'}s that satisfy the condition -$j*j>2*k$, and then compares the size of that list with $3$. In -this way, we impose the requirement that there should be at least -$3$ values of $j$. We can see how the Scala code closely follows -the mathematical formulation of the task. +computes a list of all $j$\textsf{'}s that satisfy the condition $j*j>2*k$. +The size of that list is then compared with $3$. In this way, we +impose the requirement that there should be at least $3$ values of +$j$. We can see how the Scala code closely follows the mathematical +formulation of the task. \section{Summary} @@ -900,10 +865,9 @@ \section{Summary} \begin{itemize} \item Example~1: Compute the smallest $n\geq1$ such that $f(f(f(...f(0)...)))\geq1000$, where the given function $f$ is applied $n$ times. -\item Example~2: Compute a floating-point number from a list of characters -showing the decimal representation of that number. For example, the -list $\left["1","2",".","3"\right]$ should be transformed into the -number $12.3$. +\item Example~2: Compute a list of partial sums from a given list of integers. +For example, the list $\left[1,2,3,4\right]$ should be transformed +into $\left[1,3,6,10\right]$. \item Example~3: Perform binary search over a sorted list of integers. \end{itemize} These computations require a general case of \emph{mathematical induction}\index{mathematical induction}. @@ -925,13 +889,10 @@ \section{Summary} as a formula by using mathematical induction, but we have not yet seen how to implement that in Scala code. -Example\ 2 corresponds to the function \lstinline!digitsToDouble! -(Example~\ref{subsec:Example-4-digitstodouble-foldleft}). The input -is processed one character at a time. At each step, the floating-point -result is updated depending on the previous result and on whether -a decimal point has already been seen. Operations such as \texttt{}\lstinline!map! -and \texttt{}\lstinline!filter! cannot compute the next element -of a sequence depending on previous values +An implementation of Example\ 2 is shown in Section~\ref{sec:Transforming-a-sequence}. +This cannot be implemented with operations such as \texttt{}\lstinline!map! +and \texttt{}\lstinline!filter! because they cannot produce sequences +whose next elements depend on previous values. Example\ 3 defines the search result by induction: the list is split in half, and search is performed recursively (i.e., using the inductive @@ -973,12 +934,12 @@ \subsubsection{Exercise \label{subsec:ch1-aggr-Example-6}\ref{subsec:ch1-aggr-Ex \subsubsection{Exercise \label{subsec:ch1-aggr-Exercise-2}\ref{subsec:ch1-aggr-Exercise-2}} Using the function \lstinline!isPrime!, check numerically the Euler -product\index{Euler product} formula\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Proof_of_the_Euler_product_formula_for_the_Riemann_zeta_function}{https://en.wikipedia.org/wiki/Proof\_of\_the\_Euler\_product\_formula\_for\_the\_Riemann\_zeta\_function}}} +product\index{Euler product} formula\footnote{\texttt{\href{http://tinyurl.com/4rjj2rvc}{http://tinyurl.com/4rjj2rvc}}} for the Riemann\textsf{'}s zeta function\index{Riemann\textsf{'}s zeta function} $\zeta\left(4\right)$. It is known\footnote{\texttt{\href{https://ocw.mit.edu/courses/mathematics/18-104-seminar-in-analysis-applications-to-number-theory-fall-2006/projects/chan.pdf}{https://tinyurl.com/yxey4tsd}}} that $\zeta\left(4\right)=\frac{\pi^{4}}{90}$: \[ -\zeta\left(4\right)=\prod_{k\geq2;~k\text{ is prime}}\frac{1}{1-\frac{1}{p^{4}}}=\frac{\pi^{4}}{90}\quad. +\zeta\left(4\right)=\prod_{k\geq2;~k\text{ is prime}}\frac{1}{1-\frac{1}{k^{4}}}=\frac{\pi^{4}}{90}\quad. \] @@ -997,27 +958,27 @@ \subsubsection{Exercise \label{subsec:ch1-transf-Exercise-1}\ref{subsec:ch1-tran \subsubsection{Exercise \label{subsec:ch1-transf-Exercise-2}\ref{subsec:ch1-transf-Exercise-2}} An integer $n$ is called a \textsf{``}$3$-factor\textsf{''} if it is divisible by -only three different integers $j$ such that $2\leq j Boolean!, an integer $n$ is called a \textsf{``}$3$-$f$\textsf{''} if there are only three different integers -$j\in[1,...,n]$ such that $f(j)$ returns \lstinline!true!. Define -a function that takes $f$ as an argument and returns a sequence of -all \textsf{``}$3$-$f$\textsf{''} integers among $n\in[1,...,1000]$. What is the -type of that function? Implement Exercise~\ref{subsec:ch1-transf-Exercise-2} +$1 List[List[Int]]! +Define a function \lstinline!at100! of type \texttt{}\lstinline!List[List[Int]] => List[List[Int]]! that selects only those inner lists whose largest value is at least $100$. Test with: \begin{lstlisting} -scala> see100( List( List(0, 1, 100), List(60, 80), List(1000) ) ) +scala> at100( List( List(0, 1, 100), List(60, 80), List(1000) ) ) res0: List[List[Int]] = List(List(0, 1, 100), List(1000)) \end{lstlisting} @@ -1025,13 +986,13 @@ \subsubsection{Exercise \label{subsec:ch1-transf-Exercise-4}\ref{subsec:ch1-tran \subsubsection{Exercise \label{subsec:ch1-transf-Exercise-5}\ref{subsec:ch1-transf-Exercise-5}} Define a function of type \texttt{}\lstinline!List[Double] => List[Double]! -that \textsf{``}normalizes\textsf{''} the list: it finds the element having the largest -absolute value and, if that value is nonzero, divides all elements -by that value and returns a new list; otherwise returns the original -list. Test with: +that performs a \textsf{``}normalization\textsf{''} of a list: it finds the element +having the largest absolute value and, if that value is zero, returns +the original list; if that value is nonzero, divides all elements +by that value and returns a new list. Test with: \begin{lstlisting} -scala> normalize(List(1.0, 4.0, 2.0)) -res0: List[Double] = List(0.25, 1.0, 0.5) +scala> normalize(List(1.0, -4.0, 2.0)) +res0: List[Double] = List(0.25, -1.0, 0.5) \end{lstlisting} @@ -1045,9 +1006,9 @@ \subsection{Functional programming as a paradigm} specific ways, applicable to a wide range of tasks. The main idea of FP is to write code \emph{as a mathematical expression -or formula}. This approach allows programmers to derive code through -logical reasoning rather than through guessing, similarly to how books -on mathematics reason about mathematical formulas and derive results +or formula}. This allows programmers to derive code through logical +reasoning rather than through guessing, similarly to how books on +mathematics reason about mathematical formulas and derive results systematically, without guessing or \textsf{``}debugging.\textsf{''} Like mathematicians and scientists who reason about formulas, functional programmers can \emph{reason about code} systematically and logically, based on rigorous @@ -1065,27 +1026,27 @@ \subsection{Functional programming as a paradigm} quite closely to mathematical formulas (although programmers do have to write out some details that are omitted in the mathematical notation). Just as in mathematics, large code expressions may be split into smaller -expressions when needed. Expressions can be easily reused, composed -in various ways, and written independently from each other. Over the -years, the FP community has developed a toolkit of functions (such -as \lstinline!map!, \texttt{}\lstinline!filter!, etc.) that proved -to be especially useful in real-life programming, although many of -them are not standard in mathematical literature. - -Mastering FP involves practicing to reason about programs as formulas -\textsf{``}translated into code\textsf{''}, building up the specific kind of applied -mathematical intuition, and getting familiar with mathematical concepts -adapted to a programmer\textsf{'}s needs. The FP community has discovered a -number of specific programming idioms founded on mathematical principles +expressions when needed. Expressions can be reused, composed in various +ways, and written independently from each other. Over the years, the +FP community has developed a toolkit of functions (such as \lstinline!map!, +\texttt{}\lstinline!filter!, \lstinline!flatMap!, etc.), which +are not standard in mathematical literature but proved to be useful +in practical programming. + +Mastering FP involves practicing to write programs as \textsf{``}formulas +translated into code\textsf{''}, building up the specific kind of applied +mathematical intuition, and getting familiar with certain mathematical +concepts adapted to a programmer\textsf{'}s needs. The FP community has discovered +a number of specific programming idioms founded on mathematical principles but driven by practical necessities of writing software. This book explains the theory behind those idioms, starting from code examples and heuristic ideas, and gradually building up the techniques of rigorous reasoning. This chapter explored the first significant idiom of FP: iterative -calculations performed without loops, in the style of mathematical -expressions. This technique can be easily used in any programming -language that supports nameless functions. +calculations performed without loops in the style of mathematical +expressions. This technique can be used in any programming language +that supports nameless functions. \subsection{The mathematical meaning of \textquotedblleft variables\textquotedblright} @@ -1189,11 +1150,11 @@ \subsection{The mathematical meaning of \textquotedblleft variables\textquotedbl \subsection{Iteration without loops} A distinctive feature of the FP paradigm is handling of iteration -without writing loops. +\emph{without} writing loops, just as iterative computations are written +in mathematical notation. -Iterative computations are ubiquitous in mathematics. As an example, -consider the formula for the standard deviation ($\sigma$) estimated -from a data sample $\left[x_{1},...,x_{n}\right]$: +As an example, consider the formula for the standard deviation ($\sigma$) +estimated from a data sample $\left[x_{1},...,x_{n}\right]$: \[ \sigma=\sqrt{\frac{1}{n-1}\sum_{i=1}^{n}x_{i}^{2}-\frac{1}{n\left(n-1\right)}\left(\sum_{i=1}^{n}x_{i}\right)^{2}}\quad. \] @@ -1264,23 +1225,21 @@ \subsection{Nameless functions in mathematical notation\label{subsec:Nameless-fu \begin{lstlisting} integration(0, x, { z => 1.0 / (1 + z) } ) \end{lstlisting} -Now consider the traditional mathematical notation for summation, -for instance: -\[ -\sum_{k=0}^{x}\frac{1}{1+k}\quad. -\] -In that sum, the bound variable $k$ is introduced under the $\sum$ -symbol; but in integrals, the bound variable follows the special symbol -\textsf{``}$d$\textsf{''}. This notational inconsistency could be removed if we were -to use nameless functions explicitly, for example: + +Now compare the mathematical notations for integration and for summation: +$\int_{0}^{x}\frac{dz}{1+z}$ and $\sum_{k=0}^{100}\frac{1}{1+k}$. +The integral defines a bound variable $z$ via the special symbol +\textsf{``}$d$\textsf{''}, while the summation places a bound variable $k$ in a +subscript under $\sum$. The notation could be made more consistent +by using nameless functions explicitly, for example like this: \begin{align*} \text{denote summation by }\sum_{0}^{x} & \left(k\rightarrow\frac{1}{1+k}\right)\text{ instead of }\sum_{k=0}^{x}\frac{1}{1+k}\quad,\\ \text{denote integration by }\int_{0}^{x} & \left(z\rightarrow\frac{1}{1+z}\right)\text{ instead of }\int_{0}^{x}\frac{dz}{1+z}\quad. \end{align*} -In this notation, the new summation symbol $\sum_{0}^{x}$ does not +In the new notation, the summation symbol $\sum_{0}^{x}$ does not mention the name \textsf{``}$k$\textsf{''} but takes a function as an argument. Similarly, -the new integration symbol $\int_{0}^{x}$ does not mention \textsf{``}$z$\textsf{''} -and does not use the special symbol \textsf{``}$d$\textsf{''} but now takes a function +the integration symbol $\int_{0}^{x}$ does not mention \textsf{``}$z$\textsf{''} +and does not use the special symbol \textsf{``}$d$\textsf{''} but takes a function as an argument. Written in this way, the operations of summation and integration become \emph{functions} that take functions as arguments. The above summation may be written in a consistent and straightforward @@ -1299,17 +1258,16 @@ \subsection{Nameless functions in mathematical notation\label{subsec:Nameless-fu Integration requires longer code since the computations are more complicated. \index{Simpson\textsf{'}s rule}Simpson\textsf{'}s rule\footnote{\texttt{\href{https://en.wikipedia.org/wiki/Simpson\%27s_rule}{https://en.wikipedia.org/wiki/Simpson\%27s\_rule}}} -is an algorithm for approximate numerical integration, defined by -the formulas: +gives the following formulas for numerical integration: \begin{align*} -\text{integration}\left(a,b,g,\varepsilon\right) & =\frac{\delta}{3}\big(g(a)+g(b)+4s_{1}+2s_{2}\big)\quad,\\ -\text{where }~~~n & =2\left\lfloor \frac{b-a}{\varepsilon}\right\rfloor ,\quad\delta_{x}=\frac{b-a}{n}\quad,\\ +\text{Simpson}\left(a,b,g,\varepsilon\right) & =\frac{\delta}{3}\big(g(a)+g(b)+4s_{1}+2s_{2}\big)\quad,\\ +\text{where }~~~n & =2\left\lfloor \frac{b-a}{\varepsilon}\right\rfloor \quad,\quad\quad\delta_{x}=\frac{b-a}{n}\quad,\\ s_{1}=\sum_{k=1,3,...,n-1}g(a+k\delta_{x}) & \quad,\quad\quad s_{2}=\sum_{k=2,4,...,n-2}g(a+k\delta_{x})\quad. \end{align*} - Here is a straightforward line-by-line translation of these formulas -into Scala, with some tests: +Here is a straightforward line-by-line translation of these formulas +into Scala: \begin{lstlisting} -def integration(a: Double, b: Double, g: Double => Double, eps: Double): Double = { +def simpson(a: Double, b: Double, g: Double => Double, eps: Double): Double = { // First, we define some helper values and functions corresponding // to the definitions "where n = ..." in the mathematical formulas. val n: Int = 2 * ((b - a) / eps).toInt @@ -1320,10 +1278,10 @@ \subsection{Nameless functions in mathematical notation\label{subsec:Nameless-fu delta_x / 3 * (g(a) + g(b) + 4 * s1 + 2 * s2) } -scala> integration(0, 5, x => x*x*x*x, eps = 0.01) // The exact answer is 625. +scala> simpson(0, 5, x => x*x*x*x, eps = 0.01) // The answer is 625. res0: Double = 625.0000000004167 -scala> integration(0, 7, x => x*x*x*x*x*x, eps = 0.01) // The exact answer is 117649. +scala> simpson(0, 7, x => x*x*x*x*x*x, eps = 0.01) // The answer is 117649. res1: Double = 117649.00000014296 \end{lstlisting} @@ -1342,12 +1300,13 @@ \subsection{Named and nameless expressions and their uses} situation where we take the absence of names for granted. In today\textsf{'}s programming languages, we may directly write expressions -such as \texttt{}\lstinline!(x+123)*y/(4+x)!. Note that the entire -expression does not need to have a name. Parts of that expression -(e.g., the sub-expressions \texttt{}\lstinline!x+123! or \lstinline!4+x!) -also do not have separate names. It would be inconvenient if we \emph{needed} -to assign a name to each sub-expression. The code for \lstinline!(x+123)*y/(4+x)! -would then look like this: +such as \texttt{}\lstinline!(x + 123) * y / (4 + x)!. Note that +the entire expression does not need to have a name. Parts of that +expression (e.g., the sub-expressions \texttt{}\lstinline!x + 123! +or \lstinline!4 + x!) also do not have separate names. It would be +inconvenient if we \emph{needed} to assign a name to each sub-expression. +The code for \lstinline!(x + 123) * y / (4 + x)! would look like +this: \begin{lstlisting} { @@ -1361,26 +1320,25 @@ \subsection{Named and nameless expressions and their uses} } \end{lstlisting} -This style of programming resembles assembly languages\index{assembly language}, -where every sub-expression \textemdash{} that is, every step of every -calculation \textemdash{} must be assigned a separate memory address -or a CPU register. +This style of programming resembles assembly languages\index{assembly language}: +every sub-expression \textemdash{} that is, every step of every calculation +\textemdash{} needs to be written into a separate memory address or +a CPU register. Programmers become more productive when their programming language supports nameless expressions. This is also common practice in mathematics; names are assigned when needed, but most expressions remain nameless. -It is also useful if data structures can be created without names. -For instance, a \textbf{dictionary}\index{dictionary} (also called -a \textsf{``}map\textsf{''} or a \textsf{``}hashmap\textsf{''}) may be created in Scala with this -code: +It is also useful to be able to create nameless data structures. For +instance, a \textbf{dictionary}\index{dictionary} (also called a +\textsf{``}map\textsf{''} or a \textsf{``}hashmap\textsf{''}) is created in Scala with this code: \begin{lstlisting} Map("a" -> 1, "b" -> 2, "c" -> 3) \end{lstlisting} This is a nameless expression whose value is a dictionary. In programming languages that do not have such a construction, programmers have to write special code that creates an initially empty dictionary and -then fills it step by step with values: +then fills in one value at a time: \begin{lstlisting}[language=Java] // Scala code creating a dictionary: Map("a" -> 1, "b" -> 2, "c" -> 3) @@ -1399,9 +1357,8 @@ \subsection{Named and nameless expressions and their uses} \subsection{Historical perspective on nameless functions} -\begin{wraptable}{r}{0.6\columnwidth}% +\begin{table} \begin{centering} -\vspace{-1\baselineskip} \begin{tabular}{|c|c|c|} \hline \textbf{\small{}Language} & \textbf{\small{}Year} & \textbf{\small{}Code for }{\small{}$k\rightarrow k+1$}\tabularnewline @@ -1413,10 +1370,14 @@ \subsection{Historical perspective on nameless functions} \hline {\footnotesize{}LISP} & {\footnotesize{}1958} & \texttt{\footnotesize{}}\lstinline!(lambda (k) (+ k 1))!\tabularnewline \hline +{\footnotesize{}ALGOL 68} & {\footnotesize{}1968} & \texttt{\footnotesize{}}\lstinline!(INT k) INT: k + 1!\tabularnewline +\hline {\footnotesize{}Standard ML} & {\footnotesize{}1973} & \texttt{\footnotesize{}}\lstinline!fn (k: int) => k + 1!\tabularnewline \hline {\footnotesize{}Caml} & {\footnotesize{}1985} & \lstinline!fun (k: int) -> k + 1!\tabularnewline \hline +{\footnotesize{}Erlang} & {\footnotesize{}1986} & \lstinline!fun(K) -> K + 1 end!\tabularnewline +\hline {\footnotesize{}Haskell} & {\footnotesize{}1990} & \lstinline!\ k -> k + 1!\tabularnewline \hline {\footnotesize{}Oz} & {\footnotesize{}1991} & \lstinline!fun {$ K} K + 1!\tabularnewline @@ -1449,7 +1410,7 @@ \subsection{Historical perspective on nameless functions} \hline {\footnotesize{}Kotlin} & {\footnotesize{}2012} & \lstinline!{ k: Int -> k + 1 }!\tabularnewline \hline -{\footnotesize{}Swift} & {\footnotesize{}2014} & \lstinline!{ (k:int) -> int in return k + 1 }!\tabularnewline +{\footnotesize{}Swift} & {\footnotesize{}2014} & \lstinline!{ (k: int) -> int in return k + 1 }!\tabularnewline \hline {\footnotesize{}Java 8} & {\footnotesize{}2014} & \lstinline!(int k) -> k + 1!\tabularnewline \hline @@ -1457,24 +1418,27 @@ \subsection{Historical perspective on nameless functions} \hline \end{tabular} \par\end{centering} -\caption{Nameless functions in programming languages.\label{lambda-functions-table}} -\vspace{-3\baselineskip} -\end{wraptable}% +\caption{Nameless functions in various programming languages.\label{lambda-functions-table}} + +\end{table} + +What this book calls (for clarity) a \textsf{``}nameless function\textsf{''} is also +known as an anonymous function,\index{anonymous function!see \textsf{``}nameless functions\textsf{''}} +a function expression, a function literal, a closure, a \index{lambda-function!see \textsf{``}nameless function\textsf{''}}lambda +function, a lambda expression, or just a \textsf{``}lambda\textsf{''}. Nameless functions were first used in 1936 in a theoretical programming language called \textsf{``}$\lambda$-calculus\index{\$lambda\$@$\lambda$-calculus}\textsf{''}. In that language,\footnote{Although called a \textsf{``}calculus,\textsf{''} it is a (drastically simplified) \emph{programming} \emph{language}, not related to differential or -integral calculus. Also, the symbol $\lambda$ has no particular significance -other than a syntactic separator used in functions. Practitioners -of functional programming usually do not need to study any $\lambda$-calculus. -The practically relevant knowledge that comes from $\lambda$-calculus -will be explained in Chapter~\ref{chap:Higher-order-functions}.} all functions are nameless and have a single argument. The Greek -letter $\lambda$ (pronounced as \textsf{``}lambda\textsf{''}) is a syntax separator -that denotes function arguments in nameless functions. For example, -the nameless function $x\rightarrow x+1$ could be written as $\lambda x.~add~x~1$ -in $\lambda$-calculus if it had a function $add$ for adding integers -(but it does not). +integral calculus. Practitioners of functional programming do not +need to study the theory of $\lambda$-calculus. The practically relevant +knowledge that comes from $\lambda$-calculus will be explained in +Chapter~\ref{chap:Higher-order-functions}.} all functions are nameless and have a single argument. The Greek +letter $\lambda$ is a syntax separator that denotes function arguments +in nameless functions. For example, the nameless function $x\rightarrow x+1$ +would be written as $\lambda x.~add~x~1$ in $\lambda$-calculus if +it had a function $add$ for adding integers (but it does not). In most programming languages that were in use until around 1990, all functions required names. But by 2015, the use of nameless functions @@ -1482,9 +1446,3 @@ \subsection{Historical perspective on nameless functions} turned out to be so productive that most newly created languages included nameless functions, while older languages added that feature. Table~\ref{lambda-functions-table} shows the year when various languages supported nameless functions. - -What this book calls a \textsf{``}nameless function\textsf{''} is also known as an -anonymous function,\index{anonymous function!see \textsf{``}nameless functions\textsf{''}} -a function expression, a function literal, a closure, a \index{lambda-function!see \textsf{``}nameless function\textsf{''}}lambda -function, a lambda expression, or just a \textsf{``}lambda\textsf{''}. This book prefers -the clearer term \textsf{``}nameless function\textsf{''}. diff --git a/sofp-src/sofp-preface.lyx b/sofp-src/sofp-preface.lyx index 694c30e59..6d9764866 100644 --- a/sofp-src/sofp-preface.lyx +++ b/sofp-src/sofp-preface.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -298,10 +313,7 @@ Preface This book is a reference text and a tutorial that teaches functional programmers how to reason mathematically about types and code, in a manner directly relevant to software practice. -\end_layout - -\begin_layout Standard -The material ranges from introductory to advanced. + The material ranges from introductory to advanced. \begin_inset Note Comment status collapsed @@ -314,8 +326,9 @@ Readers will need to learn some difficult concepts through prolonged mental \end_inset -The book assumes a certain amount of mathematical experience, at the level - of familiarity with undergraduate algebra or calculus. +The book assumes a certain amount of mathematical experience, at about the + level of undergraduate algebra or calculus, as well as some experience + writing code in general-purpose programming languages. \end_layout \begin_layout Standard @@ -326,14 +339,13 @@ The vision of this book is to explain the mathematical theory that guides usage. For instance, the laws for standard typeclasses (functors, monads, etc.) are first motivated heuristically through code examples. - Only then the laws are formulated as mathematical equations and proved - rigorously. + Then the laws are formulated as mathematical equations and proved rigorously. \end_layout \begin_layout Standard -To achieve a clearer presentation of the material, the book uses some non-standa -rd notations (see Appendix +To achieve a clearer presentation of the material, the book uses certain + non-standard notations (see Appendix \begin_inset space ~ \end_inset @@ -365,12 +377,12 @@ noprefix "false" The presentation is self-contained, defining and explaining all required techniques, notations, and Scala features. Although the code examples are in Scala, the material in this book also - applies to most other functional programming languages. + applies to many other functional programming languages. \end_layout \begin_layout Standard -All concepts and techniques are motivated, illustrated by solved examples, - and explained as simply as possible ( +All concepts and techniques are motivated, illustrated by examples and explained + as simply as possible ( \begin_inset Quotes eld \end_inset @@ -380,34 +392,18 @@ but not simpler , as Einstein said). Exercises should be attempted after absorbing the preceding material. - More difficult examples and exercises are marked by an asterisk (*). \end_layout \begin_layout Standard -A software engineer needs to know only a few fragments of mathematical theory - that will answer questions arising in the practice of functional programming. - So, this book keeps theoretical material at the minimum; +A software engineer needs to learn only those few fragments of mathematical + theory that answer questions arising in the practice of functional programming. + So, this book keeps theoretical material at the minimum: \emph on vita brevis, ars longa \emph default . - Chapter -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "chap:Applied-functional-type" -plural "false" -caps "false" -noprefix "false" - -\end_inset - - discusses the scope of the required theory. - The required mathematical knowledge is limited to first notions of set - theory, formal logic, and category theory. + The scope of the required mathematical knowledge is limited to first notions + of set theory, formal logic, and category theory. Concepts such as functors or natural transformations arise organically from the practice of reasoning about code and are first introduced without reference to category theory. @@ -467,7 +463,7 @@ topoi \begin_inset Quotes erd \end_inset -, as learning these concepts will neither help programmers write code nor +, as learning those concepts will neither help programmers write code nor answer any practical questions about code. \end_layout @@ -516,7 +512,7 @@ noprefix "false" \end_inset -) for unfamiliar terminology and then start the book with Chapter +) for unfamiliar terminology and then start reading Chapter \begin_inset space ~ \end_inset @@ -616,8 +612,9 @@ noprefix "false" \end_layout \begin_layout Standard -All code examples are intended only for explanation and illustration. - As a rule, the code is not optimized for performance or stack safety. +All code examples have been tested to work but are intended only for explanation + and illustration. + As a rule, the code is not optimized for performance. \end_layout \begin_layout Standard @@ -681,7 +678,7 @@ aggregation \series default - is a function from a sequence of values to a + is a function from a collection of values to a \emph on single \emph default @@ -1001,14 +998,14 @@ noprefix "false" \end_layout \begin_layout Itemize -Frequently used methods of standard typeclasses, such as +Frequently used methods of standard typeclasses, named in Scala as \begin_inset listings inline true status open \begin_layout Plain Layout -pure +flatten \end_layout \end_inset @@ -1032,18 +1029,6 @@ status open \begin_layout Plain Layout -flatten -\end_layout - -\end_inset - -, -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - filter \end_layout @@ -1051,7 +1036,7 @@ filter , etc., are denoted by shorter words and are labeled by the type constructor they belong to. - For instance, the book talks about the methods + For instance, the methods \begin_inset listings inline true status open @@ -1091,7 +1076,7 @@ flatMap \begin_inset Formula $M$ \end_inset - but denotes the same methods by + are denoted by \begin_inset Formula $\text{pu}_{M}$ \end_inset @@ -1126,14 +1111,28 @@ next \end_inset -A green underline is sometimes also used at the last step of a derivation, +When the two-column presentation becomes too wide, the explanations are + placed before the next step's line: +\begin_inset Formula +\begin{align*} + & \quad\text{expect to equal }\text{pu}_{M}:\quad\\ + & \gunderline{\text{pu}_{M}^{\uparrow\text{Id}}}\bef\text{pu}_{M}\bef\text{ftn}_{M}\\ + & \quad\text{lifting to the identity functor}:\quad\\ + & =\text{pu}_{M}\bef\gunderline{\text{pu}_{M}\bef\text{ftn}_{M}}\\ + & \quad\text{left identity law of }M:\quad\\ + & =\text{pu}_{M}\quad. +\end{align*} + +\end_inset + + A green underline is sometimes also used at the last step of a derivation, to indicate the sub-expression that resulted from the most recent rewriting. - Other than providing hints to help remember the steps, the green underlines - + Other than providing hints to help remember the steps, the green text + and the green underlines \emph on play no role \emph default - in symbolic calculations. + in symbolic derivations. \end_layout \begin_layout Itemize @@ -1141,8 +1140,8 @@ The symbol \begin_inset Formula $\square$ \end_inset - is used occasionally to indicate the end of a definition, a derivation, - or a proof. + is used occasionally to indicate more clearly the end of a definition, + a derivation, or a proof. \end_layout \end_body diff --git a/sofp-src/sofp-preface.tex b/sofp-src/sofp-preface.tex index 826a95bdb..0c12314da 100644 --- a/sofp-src/sofp-preface.tex +++ b/sofp-src/sofp-preface.tex @@ -6,50 +6,48 @@ \global\long\def\bbnum#1{\custombb{#1}}% This book is a reference text and a tutorial that teaches functional programmers how to reason mathematically about types and code, in -a manner directly relevant to software practice. - -The material ranges from introductory to advanced. % +a manner directly relevant to software practice. The material ranges +from introductory to advanced. % \begin{comment} Readers will need to learn some difficult concepts through prolonged mental concentration and effort. \end{comment} -The book assumes a certain amount of mathematical experience, at the -level of familiarity with undergraduate algebra or calculus. +The book assumes a certain amount of mathematical experience, at about +the level of undergraduate algebra or calculus, as well as some experience +writing code in general-purpose programming languages. The vision of this book is to explain the mathematical theory that guides the practice of functional programming. So, all mathematical developments in this book are motivated by practical programming issues and are accompanied by Scala code illustrating their usage. For instance, the laws for standard typeclasses (functors, monads, etc.) are first -motivated heuristically through code examples. Only then the laws -are formulated as mathematical equations and proved rigorously. +motivated heuristically through code examples. Then the laws are formulated +as mathematical equations and proved rigorously. -To achieve a clearer presentation of the material, the book uses some +To achieve a clearer presentation of the material, the book uses certain non-standard notations (see Appendix~\ref{chap:Appendix-Notations}) and terminology (Appendix~\ref{chap:Appendix-Glossary-of-terms}). The presentation is self-contained, defining and explaining all required techniques, notations, and Scala features. Although the code examples -are in Scala, the material in this book also applies to most other +are in Scala, the material in this book also applies to many other functional programming languages. -All concepts and techniques are motivated, illustrated by solved examples, +All concepts and techniques are motivated, illustrated by examples and explained as simply as possible (\textsf{``}but not simpler\textsf{''}, as Einstein said). Exercises should be attempted after absorbing the preceding -material. More difficult examples and exercises are marked by an asterisk -({*}). +material. -A software engineer needs to know only a few fragments of mathematical -theory that will answer questions arising in the practice of functional -programming. So, this book keeps theoretical material at the minimum; -\emph{vita brevis, ars longa}. Chapter~\ref{chap:Applied-functional-type} -discusses the scope of the required theory. The required mathematical +A software engineer needs to learn only those few fragments of mathematical +theory that answer questions arising in the practice of functional +programming. So, this book keeps theoretical material at the minimum: +\emph{vita brevis, ars longa}. The scope of the required mathematical knowledge is limited to first notions of set theory, formal logic, and category theory. Concepts such as functors or natural transformations arise organically from the practice of reasoning about code and are first introduced without reference to category theory. This book does not use \textsf{``}introduction/elimination rules\textsf{''}, \textsf{``}strong normalization\textsf{''}, \textsf{``}complete partial orders\textsf{''}, \textsf{``}adjoint functors\textsf{''}, \textsf{``}pullbacks\textsf{''}, -\textsf{``}co-ends\textsf{''}, or \textsf{``}topoi\textsf{''}, as learning these concepts will neither +\textsf{``}co-ends\textsf{''}, or \textsf{``}topoi\textsf{''}, as learning those concepts will neither help programmers write code nor answer any practical questions about code. @@ -63,7 +61,7 @@ The first part of the book is for beginners in functional programming. Readers already familiar with functional programming could skim the glossary (Appendix~\ref{chap:Appendix-Glossary-of-terms}) for unfamiliar -terminology and then start the book with Chapter~\ref{chap:5-Curry-Howard}. +terminology and then start reading Chapter~\ref{chap:5-Curry-Howard}. Chapters~\ref{chap:5-Curry-Howard}\textendash \ref{chap:Functors,-contrafunctors,-and} begin using the code notation, such as Eq.~(\ref{eq:f-functor-exponential-def-of-fmap}). @@ -73,8 +71,9 @@ which summarizes the code notation more systematically and clarifies it with additional examples. -All code examples are intended only for explanation and illustration. -As a rule, the code is not optimized for performance or stack safety. +All code examples have been tested to work but are intended only for +explanation and illustration. As a rule, the code is not optimized +for performance. The author thanks Joseph Kim and Jim Kleck for doing some of the exercises and reporting some errors in earlier versions of this book. The author @@ -92,7 +91,7 @@ at that place in the text. Italics means logical emphasis. Example: \end{itemize} \begin{quotation} -An \textbf{aggregation\index{aggregation}} is a function from a sequence +An \textbf{aggregation\index{aggregation}} is a function from a collection of values to a \emph{single} value. \end{quotation} \begin{itemize} @@ -128,13 +127,13 @@ \lstinline!L[A]!. The symbol $\bef$ denotes the forward composition of functions (Scala\textsf{'}s method \lstinline!andThen!). Appendix~\ref{chap:Appendix-Notations} summarizes this book\textsf{'}s chosen notation for types and code. -\item Frequently used methods of standard typeclasses, such as \lstinline!pure!, -\lstinline!flatMap!, \lstinline!flatten!, \lstinline!filter!, etc., +\item Frequently used methods of standard typeclasses, named in Scala as +\lstinline!flatten!, \lstinline!flatMap!, \lstinline!filter!, etc., are denoted by shorter words and are labeled by the type constructor -they belong to. For instance, the book talks about the methods \lstinline!pure!, -\lstinline!flatten!, and \lstinline!flatMap! for a monad $M$ but -denotes the same methods by $\text{pu}_{M}$, $\text{ftn}_{M}$, and -$\text{flm}_{M}$ when writing code formulas and proofs of laws. +they belong to. For instance, the methods \lstinline!pure!, \lstinline!flatten!, +and \lstinline!flatMap! for a monad $M$ are denoted by $\text{pu}_{M}$, +$\text{ftn}_{M}$, and $\text{flm}_{M}$ when writing code formulas +and proofs of laws. \item Derivations are written in a two-column format. The right column contains formulas in the code notation. The left column gives an explanation or indicates the property or law used to derive the expression at @@ -145,11 +144,22 @@ {\color{greenunder}\text{lifting to the identity functor}:}\quad & =\text{pu}_{M}\bef\gunderline{\text{pu}_{M}\bef\text{ftn}_{M}}\\ {\color{greenunder}\text{left identity law of }M:}\quad & =\text{pu}_{M}\quad. \end{align*} -A green underline is sometimes also used at the last step of a derivation, +When the two-column presentation becomes too wide, the explanations +are placed before the next step\textsf{'}s line: +\begin{align*} + & \quad{\color{greenunder}\text{expect to equal }\text{pu}_{M}:}\quad\\ + & \gunderline{\text{pu}_{M}^{\uparrow\text{Id}}}\bef\text{pu}_{M}\bef\text{ftn}_{M}\\ + & \quad{\color{greenunder}\text{lifting to the identity functor}:}\quad\\ + & =\text{pu}_{M}\bef\gunderline{\text{pu}_{M}\bef\text{ftn}_{M}}\\ + & \quad{\color{greenunder}\text{left identity law of }M:}\quad\\ + & =\text{pu}_{M}\quad. +\end{align*} + A green underline is sometimes also used at the last step of a derivation, to indicate the sub-expression that resulted from the most recent rewriting. Other than providing hints to help remember the steps, -the green underlines \emph{play no role} in symbolic calculations. -\item The symbol $\square$ is used occasionally to indicate the end of -a definition, a derivation, or a proof. +the green text and the green underlines \emph{play no role} in symbolic +derivations. +\item The symbol $\square$ is used occasionally to indicate more clearly +the end of a definition, a derivation, or a proof. \end{itemize} diff --git a/sofp-src/sofp-reasoning.lyx b/sofp-src/sofp-reasoning.lyx index 06e3ed13f..a45bd71bd 100644 --- a/sofp-src/sofp-reasoning.lyx +++ b/sofp-src/sofp-reasoning.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -469,7 +484,7 @@ Code notation \begin_layout Plain Layout \size small -use a constant +use constant \end_layout \end_inset @@ -573,7 +588,7 @@ status open \begin_layout Plain Layout \size small -use a given argument +use argument \end_layout \end_inset @@ -624,7 +639,7 @@ def f(x: A) = { ... \begin_layout Plain Layout \size small -create a function +create function \end_layout \end_inset @@ -673,7 +688,7 @@ status open \begin_layout Plain Layout \size small -use a function +use function \end_layout \end_inset @@ -738,7 +753,7 @@ x.pipe(f) \begin_layout Plain Layout \size small -create a tuple +create tuple \end_layout \end_inset @@ -787,7 +802,7 @@ val p: (A, B) = (a, b) \begin_layout Plain Layout \size small -use a tuple +use tuple \end_layout \end_inset @@ -852,7 +867,7 @@ p._2 \begin_layout Plain Layout \size small -create a disjunctive value +create co-product \end_layout \end_inset @@ -917,7 +932,7 @@ Right[A, B](y) \begin_layout Plain Layout \size small -use a disjunctive value +use co-product \end_layout \end_inset @@ -1035,7 +1050,7 @@ B & y^{:B}\rightarrow g(y) \begin_layout Plain Layout \size small -use a recursive call +recursive call \end_layout \end_inset @@ -1251,41 +1266,18 @@ constant function \series bold constant functions \series default - and are denoted by: +. \end_layout \begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - +Scala example: \begin_inset listings inline false status open \begin_layout Plain Layout -def c_1[A](x: A): Int = 123 -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -35baselineskip% -\end_inset - - +def f[A](x: A): Int = 123 \end_layout \end_inset @@ -1294,26 +1286,15 @@ def c_1[A](x: A): Int = 123 \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -125baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ -c_{1}(x^{:A})\triangleq123\quad\text{or}\quad c_{1}\triangleq\_^{:A}\rightarrow123\quad. +f(x^{:A})\triangleq123\quad\quad\text{or}\quad\quad f\triangleq\_^{:A}\rightarrow123\quad. \] \end_inset -\begin_inset VSpace -125baselineskip% -\end_inset - - \end_layout \begin_layout Paragraph @@ -1336,37 +1317,14 @@ bound variable \end_layout \begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - +Scala example: \begin_inset listings inline false status open \begin_layout Plain Layout -def c_2[A](x: String, y: Int): Int = 123 + y + y -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - +def f[A](x: String, y: Int): Int = 123 + y + y \end_layout \end_inset @@ -1375,26 +1333,15 @@ def c_2[A](x: String, y: Int): Int = 123 + y + y \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -35baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ -c_{2}(x^{:\text{String}},y^{:\text{Int}})\triangleq123+y+y\quad. +f(x^{:\text{String}},y^{:\text{Int}})\triangleq123+y+y\quad. \] \end_inset -\begin_inset VSpace -85baselineskip% -\end_inset - - \end_layout \begin_layout Paragraph @@ -1402,7 +1349,7 @@ c_{2}(x^{:\text{String}},y^{:\text{Int}})\triangleq123+y+y\quad. \end_layout \begin_layout Standard -We can create a nameless function +We create a nameless function of the form \begin_inset listings inline true status open @@ -1509,23 +1456,8 @@ x \end_inset - is already defined. - So, we can create a nameless function: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - + is already defined outside that expression. + The Scala code for the corresponding nameless function is: \begin_inset listings inline false status open @@ -1538,29 +1470,10 @@ status open \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -35baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ x^{:\text{Int}}\rightarrow123+x+x\quad. @@ -1569,10 +1482,6 @@ x^{:\text{Int}}\rightarrow123+x+x\quad. \end_inset -\begin_inset VSpace -85baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -1613,44 +1522,44 @@ status open \end_inset will have a name clash. - As an example, consider an expression + An example is \begin_inset listings inline true status open \begin_layout Plain Layout -expr == { x => x } +x => { x => x } \end_layout \end_inset - that already contains a nameless function with bound variable +; here \begin_inset listings inline true status open \begin_layout Plain Layout -x +expr \end_layout \end_inset -. - If we want to make a function out of that expression, we could write + is \begin_inset listings inline true status open \begin_layout Plain Layout -x => { x => x } +{x => x} \end_layout \end_inset -, but such code is confusing. +. + Such code is confusing. It is helpful to avoid the name clash by renaming the bound variables inside \begin_inset listings @@ -1664,53 +1573,27 @@ expr \end_inset -, e.g., +, e.g., to \begin_inset listings inline true status open \begin_layout Plain Layout -expr == { z => z } -\end_layout - -\end_inset - -: +{ z => z } \end_layout -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% \end_inset - +. + The resulting code is written in Scala as: \begin_inset listings inline false status open \begin_layout Plain Layout -val f = { x: Int => { z: Int => z } } -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - +{ x: Int => { z: Int => z } } \end_layout \end_inset @@ -1719,26 +1602,15 @@ val f = { x: Int => { z: Int => z } } \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -35baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ -f\triangleq x^{:\text{Int}}\rightarrow z^{:\text{Int}}\rightarrow z\quad. +x^{:\text{Int}}\rightarrow z^{:\text{Int}}\rightarrow z\quad. \] \end_inset -\begin_inset VSpace -85baselineskip% -\end_inset - - \end_layout \begin_layout Paragraph @@ -1747,21 +1619,7 @@ f\triangleq x^{:\text{Int}}\rightarrow z^{:\text{Int}}\rightarrow z\quad. \begin_layout Standard If a function is already defined, we can use it by applying it to an argument. -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - + Scala example: \begin_inset listings inline false status open @@ -1779,29 +1637,10 @@ f(100) // Evaluates to 323. \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -65baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ f\triangleq x^{:\text{Int}}\rightarrow123+x+x\quad,\quad\quad f(100)=323\quad. @@ -1810,10 +1649,6 @@ f\triangleq x^{:\text{Int}}\rightarrow123+x+x\quad,\quad\quad f(100)=323\quad. \end_inset -\begin_inset VSpace -85baselineskip% -\end_inset - - \end_layout \begin_layout Paragraph @@ -1857,7 +1692,7 @@ status open \end_inset - as well as + as well as the tuple \begin_inset listings inline true status open @@ -1942,21 +1777,6 @@ p._2 ) may be used for tuples of any size. Example code defining these functions: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1994,29 +1814,10 @@ def pi_2[A, B]: ((A, B)) => B = { \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -120baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -100baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \begin{align*} \pi_{1}^{A,B} & \triangleq a^{:A}\times b^{:B}\rightarrow a\quad,\\ @@ -2041,11 +1842,7 @@ argument \end_layout \begin_layout Standard -Once a disjunctive type such as -\begin_inset Formula $A+B+C$ -\end_inset - - has been defined in Scala, its named +A disjunctive type is defined in Scala using named \begin_inset Quotes eld \end_inset @@ -2053,22 +1850,13 @@ constructors \begin_inset Quotes erd \end_inset - (i.e., case classes) are used to create values of that type: + (i.e., case classes). + The same named constructors are used to create values of that type. + In the code notation, constructor names are not written. \end_layout \begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - +Scala example: \begin_inset listings inline false status open @@ -2110,39 +1898,13 @@ val t: S = R(30) // Another value of type S. \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -0baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace 35baselineskip% -\end_inset - - -\begin_inset Formula -\[ -S\triangleq\text{Int}\times\text{Int}+\text{String}+\text{Int}\quad, -\] - -\end_inset - - +Code notation: \begin_inset Formula \begin{align*} +S & \triangleq\text{Int}\times\text{Int}+\text{String}+\text{Int}\quad,\\ s^{:S} & \triangleq10\times20+\bbnum 0^{:\text{String}}+\bbnum 0^{:\text{Int}}\quad,\\ t^{:S} & \triangleq\bbnum 0^{:\text{Int}\times\text{Int}}+\bbnum 0^{:\text{String}}+30\quad. \end{align*} @@ -2150,10 +1912,6 @@ t^{:S} & \triangleq\bbnum 0^{:\text{Int}\times\text{Int}}+\bbnum 0^{:\text{Strin \end_inset -\begin_inset VSpace -90baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -2261,22 +2019,8 @@ match \end_inset - keyword: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - + keyword. + Scala example: \begin_inset listings inline false status open @@ -2304,29 +2048,10 @@ val compute: Option[Int] => Option[Int] = { \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -165baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -145baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ \text{compute}^{:\bbnum 1+\text{Int}\rightarrow\bbnum 1+\text{Int}}\triangleq\,\begin{array}{|c||cc|} @@ -2339,10 +2064,6 @@ val compute: Option[Int] => Option[Int] = { \end_inset -\begin_inset VSpace -90baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -2516,23 +2237,8 @@ case \end_inset line as if it were a standalone partial function: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - -\begin_inset listings -inline false +\begin_inset listings +inline false status open \begin_layout Plain Layout @@ -2542,23 +2248,6 @@ status open \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Since \begin_inset listings inline true @@ -2581,21 +2270,6 @@ None \begin_layout Standard The second line is written in the form of a partial function as: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2607,23 +2281,6 @@ status open \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The pattern variable on the left side is \begin_inset listings inline true @@ -2647,21 +2304,6 @@ x \begin_layout Standard To obtain the matrix notation, we may simply write the two partial functions in the two rows: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2688,30 +2330,7 @@ val compute: Option[Int] => Option[Int] = { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -135baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ \text{compute}^{:\bbnum 1+\text{Int}\rightarrow\bbnum 1+\text{Int}}\triangleq\,\begin{array}{|c||c|} @@ -2723,15 +2342,6 @@ val compute: Option[Int] => Option[Int] = { \end_inset - -\begin_inset VSpace -90baselineskip% -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent This is already a valid matrix notation for the function \begin_inset Formula $f$ \end_inset @@ -2791,21 +2401,6 @@ Partial functions are expressed in the matrix notation by writing \end_inset in the missing rows: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2828,29 +2423,10 @@ def get[A]: Option[A] => A = { \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -135baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ \text{get}^{:\bbnum 1+A\rightarrow A}\triangleq\,\begin{array}{|c||c|} @@ -2867,10 +2443,6 @@ A & \text{id} \end_inset -\begin_inset VSpace -90baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -2887,21 +2459,6 @@ match \end_inset expression is equivalent to an application of a disjunctive function: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2934,29 +2491,10 @@ val q: Option[Int] = p match { \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -275baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -85baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ p\triangleq\bbnum 0^{:\bbnum 1}+64^{:\text{Int}}\quad,\quad q\triangleq p\triangleright\,\begin{array}{|c||cc|} @@ -2968,10 +2506,6 @@ p\triangleq\bbnum 0^{:\bbnum 1}+64^{:\text{Int}}\quad,\quad q\triangleq p\triang \end_inset - -\begin_inset VSpace -10baselineskip% -\end_inset - It is convenient to put the argument \begin_inset Formula $p$ \end_inset @@ -2980,7 +2514,19 @@ It is convenient to put the argument \emph on left \emph default - of the disjunctive function, as in the Scala code. + of the disjunctive function, resembling the Scala syntax +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +p match {...} +\end_layout + +\end_inset + +. \end_layout \begin_layout Standard @@ -3284,21 +2830,6 @@ forward \end_inset by: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -3311,29 +2842,10 @@ f andThen g == { x => g(f(x)) } \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -35baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ f\bef g\triangleq x\rightarrow g(f(x))\quad. @@ -3342,10 +2854,6 @@ f\bef g\triangleq x\rightarrow g(f(x))\quad. \end_inset -\begin_inset VSpace -85baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -3440,21 +2948,6 @@ filter \end_inset are often combined in this way: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -3466,30 +2959,7 @@ x.map(f).filter(p) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -35baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ x\triangleright\text{fmap}\,(f)\triangleright\text{filt}\,(p)\quad. @@ -3498,10 +2968,6 @@ x\triangleright\text{fmap}\,(f)\triangleright\text{filt}\,(p)\quad. \end_inset -\begin_inset VSpace -85baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -3544,8 +3010,8 @@ x\triangleright f\triangleright g=x\triangleright(f\bef g)\quad. \end_inset -Such formulas are needed often, so we follow the convention that the pipe - operation ( +Such formulas are needed often, so we introduce the convention that the + pipe operation ( \begin_inset Formula $\triangleright$ \end_inset @@ -3563,7 +3029,7 @@ pipe notation!operator precedence \end_inset - We can then omit parentheses: + We can then omit more parentheses: \begin_inset Formula $x\triangleright(f\bef g)=x\triangleright f\bef g$ \end_inset @@ -3957,21 +3423,6 @@ diagonal \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -3984,29 +3435,10 @@ def delta[A]: A => (A, A) = { x => (x, x) } \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -115baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ \Delta^{A}:A\rightarrow A\times A\quad,\quad\quad\Delta\triangleq a^{:A}\rightarrow a\times a\quad. @@ -4015,10 +3447,6 @@ def delta[A]: A => (A, A) = { x => (x, x) } \end_inset -\begin_inset VSpace -115baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -4056,21 +3484,6 @@ law \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "50col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -4083,29 +3496,10 @@ delta(x)._1 == x \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -35baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ \pi_{1}(\Delta(x))=x\quad. @@ -4114,10 +3508,6 @@ delta(x)._1 == x \end_inset -\begin_inset VSpace -85baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -4425,21 +3815,6 @@ noprefix "false" In this way, we see that all types match. We can put the resulting types into a type diagram and write the law with type annotations: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "25col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -200baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{1.6pc}\xyScaleX{4.0pc}A\ar[d]\sb(0.45){f}\ar[r]\sb(0.45){\Delta^{A}} & A\times A\ar[d]\sp(0.45){f\boxtimes f}\\ @@ -4450,26 +3825,6 @@ B\ar[r]\sp(0.45){\Delta^{B}} & B\times B \end_inset -\begin_inset VSpace -10baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -30baselineskip% -\end_inset - - \begin_inset Formula \[ f^{:A\rightarrow B}\bef\Delta^{:B\rightarrow B\times B}=\Delta^{:A\rightarrow A\times A}\bef(f\boxtimes f)\quad. @@ -4495,21 +3850,6 @@ To prove the law, we need to use the known code of the function \begin_layout Standard We will now perform this computation in the Scala syntax and in the code notation: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "54col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -4547,26 +3887,10 @@ x.pipe(delta andThen { case (a, b) => (f(a), f(b)) }) \end_inset -\begin_inset VSpace -300baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -140baselineskip% -\end_inset - - +In the code notation: \begin_inset Formula \begin{align*} & x\triangleright f\bef\Delta=f(x)\,\gunderline{\triangleright\,(b}\rightarrow b\times b)\\ @@ -4579,10 +3903,6 @@ x.pipe(delta andThen { case (a, b) => (f(a), f(b)) }) \end_inset -\begin_inset VSpace -150baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -4652,21 +3972,6 @@ merge[A] \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "46col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -4714,29 +4019,10 @@ def merge[A]: Either[A, A] => A = { \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -120baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ \text{swap}^{A}\triangleq\,\begin{array}{|c||cc|} @@ -4753,10 +4039,6 @@ A & \text{id} \end_inset -\begin_inset VSpace -40baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -4769,10 +4051,6 @@ We can quickly prove by matrix composition that \end_inset : -\begin_inset VSpace 0baselineskip% -\end_inset - - \begin_inset Formula \begin{align*} & \text{swap}\bef\text{swap}=\,\begin{array}{||cc|} @@ -4865,21 +4143,6 @@ fmap \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "60col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -4907,29 +4170,10 @@ def fmap[A, B](f: A => B): Either[A, A] => Either[B, B] = { \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -165baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -145baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ (f^{:A\rightarrow B})^{\uparrow E}\triangleq\,\begin{array}{|c||cc|} @@ -4942,10 +4186,6 @@ A & \bbnum 0 & f \end_inset -\begin_inset VSpace -70baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -4982,21 +4222,6 @@ merge \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "30col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -200baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{1.6pc}\xyScaleX{4.0pc}A+A\ar[d]\sb(0.45){f^{\uparrow E}}\ar[r]\sb(0.55){\text{merge}^{A}} & A\ar[d]\sp(0.45){f}\\ @@ -5007,26 +4232,6 @@ B+B\ar[r]\sp(0.55){\text{merge}^{B}} & B \end_inset -\begin_inset VSpace -10baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -30baselineskip% -\end_inset - - \begin_inset Formula \[ (f^{:A\rightarrow B})^{\uparrow E}\bef\text{merge}^{B}=\text{merge}^{A}\bef f^{:A\rightarrow B}\quad. @@ -5120,21 +4325,6 @@ merge \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "30col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -180baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{1.5pc}\xyScaleX{4.5pc}E^{A+A}\ar[d]\sp(0.45){\text{merge}^{\uparrow E}}\ar[r]\sp(0.55){\text{merge}^{A+A}} & A+A\ar[d]\sb(0.5){\text{merge}^{A}}\\ @@ -5145,26 +4335,6 @@ E^{A}\ar[r]\sb(0.55){\text{merge}^{A}} & A \end_inset -\begin_inset VSpace -60baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -30baselineskip% -\end_inset - - \begin_inset Formula \[ (\text{merge}^{A})^{\uparrow E}\bef\text{merge}^{A}=\text{merge}^{A+A}\bef\text{merge}^{A}\quad. @@ -5424,21 +4594,6 @@ Ignored arguments If all rows of the disjunctive function ignore their arguments and always return the same results, we may collapse all rows into one, as shown in this example: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "51col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -20baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -5471,44 +4626,23 @@ def same[A]: Either[A, Option[A]] => Option[A] = { \end_inset -\begin_inset VSpace -300baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -140baselineskip% -\end_inset - - +Code notation: \begin_inset Formula -\begin{align*} - & \text{same}^{:A+\bbnum 1+A\rightarrow\bbnum 1+A}\triangleq\,\begin{array}{|c||cc|} +\[ +\text{same}^{:A+\bbnum 1+A\rightarrow\bbnum 1+A}\triangleq\,\begin{array}{|c||cc|} & \bbnum 1 & A\\ \hline A & \_\rightarrow1 & \bbnum 0\\ \bbnum 1 & \_\rightarrow1 & \bbnum 0\\ A & \_\rightarrow1 & \bbnum 0 -\end{array}\\ - & =\,\begin{array}{|c||cc|} +\end{array}=\,\begin{array}{|c||cc|} & \bbnum 1 & A\\ \hline A+\bbnum 1+A & \_\rightarrow1 & \bbnum 0 \end{array}\quad. -\end{align*} - -\end_inset - +\] -\begin_inset VSpace -100baselineskip% \end_inset @@ -6248,21 +5382,6 @@ swap[A, B] \end_inset defined as: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "63col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -90baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -6275,29 +5394,10 @@ def swap[A, B]: ((A, B)) => (B, A) = { case (a, b) => (b, a) } \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -125baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ \text{swap}^{A,B}\triangleq a^{:A}\times b^{:B}\rightarrow b\times a\quad. @@ -6305,10 +5405,6 @@ def swap[A, B]: ((A, B)) => (B, A) = { case (a, b) => (b, a) } \end_inset - -\begin_inset VSpace -15baselineskip% -\end_inset - Show that \begin_inset Formula $\Delta\bef\text{swap}=\Delta$ \end_inset @@ -6414,21 +5510,6 @@ two[A] \end_inset with the type signature: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "63col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -6441,29 +5522,10 @@ def two[A]: ((Either[Unit, Unit], A)) => Either[A, A] = ??? \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -25baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -115baselineskip% -\end_inset - - +Code notation: \begin_inset Formula \[ \text{two}^{A}:(\bbnum 1+\bbnum 1)\times A\rightarrow A+A\quad. @@ -6471,10 +5533,6 @@ def two[A]: ((Either[Unit, Unit], A)) => Either[A, A] = ??? \end_inset - -\begin_inset VSpace -35baselineskip% -\end_inset - Implement that function and prove that it satisfies the \begin_inset Quotes eld \end_inset diff --git a/sofp-src/sofp-reasoning.tex b/sofp-src/sofp-reasoning.tex index aab19488c..6270d4480 100644 --- a/sofp-src/sofp-reasoning.tex +++ b/sofp-src/sofp-reasoning.tex @@ -30,23 +30,23 @@ \subsection{The nine constructions of fully parametric code} \textbf{\small{}Constructions} & \textbf{\small{}Scala examples} & \textbf{\small{}Code notation}\tabularnewline \hline \hline -{\small{}use a constant} & {\small{}}\lstinline!()!{\small{} or }\lstinline!true!{\small{} +{\small{}use constant} & {\small{}}\lstinline!()!{\small{} or }\lstinline!true!{\small{} or }\lstinline!"abc"!{\small{} or }\lstinline!123! & {\small{}$1$, $\text{true}$, $\text{"abc"}$, $123$}\tabularnewline \hline -{\small{}use a given argument} & {\small{}}\lstinline!def f(x: A) = { ... x ... }! & {\small{}$f(x^{:A})\triangleq...~x~...$}\tabularnewline +{\small{}use argument} & {\small{}}\lstinline!def f(x: A) = { ... x ... }! & {\small{}$f(x^{:A})\triangleq...~x~...$}\tabularnewline \hline -{\small{}create a function} & {\small{}}\lstinline!(x: A) => expr(x)! & {\small{}$x^{:A}\rightarrow\text{expr}\left(x\right)$}\tabularnewline +{\small{}create function} & {\small{}}\lstinline!(x: A) => expr(x)! & {\small{}$x^{:A}\rightarrow\text{expr}\left(x\right)$}\tabularnewline \hline -{\small{}use a function} & {\small{}}\lstinline!f(x)!{\small{} or }\lstinline!x.pipe(f)!{\small{} +{\small{}use function} & {\small{}}\lstinline!f(x)!{\small{} or }\lstinline!x.pipe(f)!{\small{} (Scala 2.13)} & {\small{}$f(x)$ or $x\triangleright f$}\tabularnewline \hline -{\small{}create a tuple} & {\small{}}\lstinline!val p: (A, B) = (a, b)! & {\small{}$p^{:A\times B}\triangleq a\times b$}\tabularnewline +{\small{}create tuple} & {\small{}}\lstinline!val p: (A, B) = (a, b)! & {\small{}$p^{:A\times B}\triangleq a\times b$}\tabularnewline \hline -{\small{}use a tuple} & {\small{}}\lstinline!p._1!{\small{} or }\lstinline!p._2! & {\small{}$p\triangleright\pi_{1}$ or $p\triangleright\pi_{2}$}\tabularnewline +{\small{}use tuple} & {\small{}}\lstinline!p._1!{\small{} or }\lstinline!p._2! & {\small{}$p\triangleright\pi_{1}$ or $p\triangleright\pi_{2}$}\tabularnewline \hline -{\small{}create a disjunctive value} & {\small{}}\lstinline!Left[A, B](x)!{\small{} or }\lstinline!Right[A, B](y)! & {\small{}$x^{:A}+\bbnum 0^{:B}$ or $\bbnum 0^{:A}+y^{:B}$}\tabularnewline +{\small{}create co-product} & {\small{}}\lstinline!Left[A, B](x)!{\small{} or }\lstinline!Right[A, B](y)! & {\small{}$x^{:A}+\bbnum 0^{:B}$ or $\bbnum 0^{:A}+y^{:B}$}\tabularnewline \hline -{\small{}use a disjunctive value} & {\small{}\hspace*{-0.013\linewidth}}% +{\small{}use co-product} & {\small{}\hspace*{-0.013\linewidth}}% \begin{minipage}[c][1\totalheight][b]{0.33\columnwidth}% {\small{}\vspace{0.14\baselineskip} } @@ -65,7 +65,7 @@ \subsection{The nine constructions of fully parametric code} B & y^{:B}\rightarrow g(y) \end{array}$}\tabularnewline \hline -{\small{}use a recursive call} & {\small{}}\lstinline!def f(x) = { ... f(y) ... }! & {\small{}$f(x)\triangleq...~\overline{f}(y)~...$}\tabularnewline +{\small{}recursive call} & {\small{}}\lstinline!def f(x) = { ... f(y) ... }! & {\small{}$f(x)\triangleq...~\overline{f}(y)~...$}\tabularnewline \hline \end{tabular} \par\end{centering} @@ -85,22 +85,17 @@ \subsection{The nine constructions of fully parametric code} may be written as $\_\rightarrow\text{expr}$. Functions that do not use their argument are called \index{constant function}\textbf{constant -functions} and are denoted by: +functions}. -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.65\baselineskip} +Scala example: \begin{lstlisting} -def c_1[A](x: A): Int = 123 +def f[A](x: A): Int = 123 \end{lstlisting} -\vspace{-0.35\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.25\baselineskip} +Code notation: \[ -c_{1}(x^{:A})\triangleq123\quad\text{or}\quad c_{1}\triangleq\_^{:A}\rightarrow123\quad. +f(x^{:A})\triangleq123\quad\quad\text{or}\quad\quad f\triangleq\_^{:A}\rightarrow123\quad. \] -\vspace{-1.25\baselineskip} \paragraph{2) Use a given argument} @@ -109,100 +104,74 @@ \subsection{The nine constructions of fully parametric code} (e.g., an argument within a function\textsf{'}s body), we may use the bound variable at any place, as many times as we need. -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.65\baselineskip} +Scala example: \begin{lstlisting} -def c_2[A](x: String, y: Int): Int = 123 + y + y +def f[A](x: String, y: Int): Int = 123 + y + y \end{lstlisting} -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.35\baselineskip} +Code notation: \[ -c_{2}(x^{:\text{String}},y^{:\text{Int}})\triangleq123+y+y\quad. +f(x^{:\text{String}},y^{:\text{Int}})\triangleq123+y+y\quad. \] -\vspace{-0.85\baselineskip} \paragraph{3) Create a function} -We can create a nameless function \lstinline!{ x => expr }! using -a variable, say \lstinline!x!, and any expression \lstinline!expr! +We create a nameless function of the form \lstinline!{ x => expr }! +using a variable, say \lstinline!x!, and any expression \lstinline!expr! that may use \lstinline!x! as a free variable\index{free variable} (i.e., a variable that should be defined outside that expression). E.g., the expression \lstinline!123 + x + x! uses \lstinline!x! as a free variable because \lstinline!123 + x + x! only makes sense -if \lstinline!x! is already defined. So, we can create a nameless -function: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.65\baselineskip} +if \lstinline!x! is already defined outside that expression. The +Scala code for the corresponding nameless function is: \begin{lstlisting} { x: Int => 123 + x + x } \end{lstlisting} -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.35\baselineskip} +Code notation: \[ x^{:\text{Int}}\rightarrow123+x+x\quad. \] -\vspace{-0.85\baselineskip} If the expression \lstinline!expr! already contains \lstinline!x! as a bound variable, the function \lstinline!{ x => expr }! will -have a name clash. As an example, consider an expression \lstinline!expr == { x => x }! -that already contains a nameless function with bound variable \lstinline!x!. -If we want to make a function out of that expression, we could write -\lstinline!x => { x => x }!, but such code is confusing. It is helpful -to avoid the name clash by renaming the bound variables inside \lstinline!expr!, -e.g., \lstinline!expr == { z => z }!: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.65\baselineskip} +have a name clash. An example is \lstinline!x => { x => x }!; here +\lstinline!expr! is \lstinline!{x => x}!. Such code is confusing. +It is helpful to avoid the name clash by renaming the bound variables +inside \lstinline!expr!, e.g., to \lstinline!{ z => z }!. The resulting +code is written in Scala as: \begin{lstlisting} -val f = { x: Int => { z: Int => z } } +{ x: Int => { z: Int => z } } \end{lstlisting} -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.35\baselineskip} +Code notation: \[ -f\triangleq x^{:\text{Int}}\rightarrow z^{:\text{Int}}\rightarrow z\quad. +x^{:\text{Int}}\rightarrow z^{:\text{Int}}\rightarrow z\quad. \] -\vspace{-0.85\baselineskip} \paragraph{4) Use a function} If a function is already defined, we can use it by applying it to -an argument. - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.65\baselineskip} +an argument. Scala example: \begin{lstlisting} val f = { x: Int => 123 + x + x } f(100) // Evaluates to 323. \end{lstlisting} -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.65\baselineskip} +Code notation: \[ f\triangleq x^{:\text{Int}}\rightarrow123+x+x\quad,\quad\quad f(100)=323\quad. \] -\vspace{-0.85\baselineskip} \paragraph{5) Create a tuple} Given two values \lstinline!a: A! and \lstinline!b: B!, we can create -the tuple \lstinline!(a, b)! as well as \lstinline!(b, a)!. In the -code notation, those tuples are written as $a\times b$ and $b\times a$. +the tuple \lstinline!(a, b)! as well as the tuple \lstinline!(b, a)!. +In the code notation, those tuples are written as $a\times b$ and +$b\times a$. \paragraph{6) Use a tuple} @@ -211,9 +180,6 @@ \subsection{The nine constructions of fully parametric code} code notation is $p\triangleright\pi_{1}$ and $p\triangleright\pi_{2}$. The auxiliary functions $\pi_{i}$ (where $i=1,2,...$) may be used for tuples of any size. Example code defining these functions: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} def pi_1[A, B]: ((A, B)) => A = { case (a, b) => a @@ -223,10 +189,7 @@ \subsection{The nine constructions of fully parametric code} } // Same as p => p._2 or _._2 \end{lstlisting} -\vspace{-1.2\baselineskip} -\end{wrapfigure}% - -~\vspace{-1\baselineskip} +Code notation: \begin{align*} \pi_{1}^{A,B} & \triangleq a^{:A}\times b^{:B}\rightarrow a\quad,\\ \pi_{2}^{A,B} & \triangleq a^{:A}\times b^{:B}\rightarrow b\quad. @@ -236,12 +199,12 @@ \subsection{The nine constructions of fully parametric code} \paragraph{7) Create a disjunctive value} -Once a disjunctive type such as $A+B+C$ has been defined in Scala, -its named \textsf{``}constructors\textsf{''} (i.e., case classes) are used to create -values of that type: +A disjunctive type is defined in Scala using named \textsf{``}constructors\textsf{''} +(i.e., case classes). The same named constructors are used to create +values of that type. In the code notation, constructor names are not +written. -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.75\baselineskip} +Scala example: \begin{lstlisting} sealed trait S final case class P(w: Int, x: Int) extends S @@ -252,18 +215,12 @@ \subsection{The nine constructions of fully parametric code} val t: S = R(30) // Another value of type S. \end{lstlisting} -\vspace{-0\baselineskip} -\end{wrapfigure}% - -~\vspace{0.35\baselineskip} -\[ -S\triangleq\text{Int}\times\text{Int}+\text{String}+\text{Int}\quad, -\] +Code notation: \begin{align*} +S & \triangleq\text{Int}\times\text{Int}+\text{String}+\text{Int}\quad,\\ s^{:S} & \triangleq10\times20+\bbnum 0^{:\text{String}}+\bbnum 0^{:\text{Int}}\quad,\\ t^{:S} & \triangleq\bbnum 0^{:\text{Int}\times\text{Int}}+\bbnum 0^{:\text{String}}+30\quad. \end{align*} -\vspace{-0.9\baselineskip} The code notation for disjunctive values, e.g., $\bbnum 0+\bbnum 0+x$, is more verbose than the Scala syntax such as \lstinline!R(x)!. The @@ -280,10 +237,7 @@ \subsection{The nine constructions of fully parametric code} Recall that functions that take a disjunctive value as an argument (\textsf{``}\index{disjunctive functions}\textbf{disjunctive functions}\textsf{''}) may be also written in Scala \emph{without} the \lstinline!match! -keyword: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.65\baselineskip} +keyword. Scala example: \begin{lstlisting} val compute: Option[Int] => Option[Int] = { case None => Some(100) @@ -291,10 +245,7 @@ \subsection{The nine constructions of fully parametric code} } \end{lstlisting} -\vspace{-1.65\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.45\baselineskip} +Code notation: \[ \text{compute}^{:\bbnum 1+\text{Int}\rightarrow\bbnum 1+\text{Int}}\triangleq\,\begin{array}{|c||cc|} & \bbnum 1 & \text{Int}\\ @@ -302,7 +253,6 @@ \subsection{The nine constructions of fully parametric code} \text{Int} & \bbnum 0 & x\rightarrow\frac{x}{2} \end{array}\quad. \] -\vspace{-0.9\baselineskip} We will use this example to explain how disjunctive functions are written in the matrix notation\index{matrix notation}\index{disjunctive type!in matrix notation}. @@ -325,49 +275,28 @@ \subsection{The nine constructions of fully parametric code} translate the \lstinline!case! expressions line by line from the Scala code. Look at the first \lstinline!case! line as if it were a standalone partial function: - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} { case None => Some(100) } \end{lstlisting} - -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -\noindent Since \lstinline!None! is a named unit, this function is -written in the code notation as $1\rightarrow\bbnum 0^{:\bbnum 1}+100^{:\text{Int}}$. +Since \lstinline!None! is a named unit, this function is written +in the code notation as $1\rightarrow\bbnum 0^{:\bbnum 1}+100^{:\text{Int}}$. The second line is written in the form of a partial function as: - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} { case Some(x) => Some(x / 2) } \end{lstlisting} - -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -\noindent The pattern variable on the left side is \lstinline!x!, -so we can denote that function by $x^{:\text{Int}}\rightarrow\bbnum 0^{:\bbnum 1}+(x/2)^{:\text{Int}}$. +The pattern variable on the left side is \lstinline!x!, so we can +denote that function by $x^{:\text{Int}}\rightarrow\bbnum 0^{:\bbnum 1}+(x/2)^{:\text{Int}}$. To obtain the matrix notation, we may simply write the two partial functions in the two rows: - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} val compute: Option[Int] => Option[Int] = { case None => Some(100) case Some(x) => Some(x / 2) } \end{lstlisting} - -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.35\baselineskip} +Code notation: \[ \text{compute}^{:\bbnum 1+\text{Int}\rightarrow\bbnum 1+\text{Int}}\triangleq\,\begin{array}{|c||c|} & \bbnum 1+\text{Int}\\ @@ -375,15 +304,13 @@ \subsection{The nine constructions of fully parametric code} \text{Int} & x\rightarrow\bbnum 0+\frac{x}{2} \end{array}\quad. \] -\vspace{-0.9\baselineskip} - -\noindent This is already a valid matrix notation for the function -$f$. So far, the matrix has two rows and one column. However, we -notice that each row\textsf{'}s return value is \emph{known} to be in a specific -part of the disjunctive type $\bbnum 1+\text{Int}$ (in this example, -both rows return values of type $\bbnum 0+\text{Int}$). So, we can -split the column into two columns and obtain a clearer and more useful -notation for this function: +This is already a valid matrix notation for the function $f$. So +far, the matrix has two rows and one column. However, we notice that +each row\textsf{'}s return value is \emph{known} to be in a specific part of +the disjunctive type $\bbnum 1+\text{Int}$ (in this example, both +rows return values of type $\bbnum 0+\text{Int}$). So, we can split +the column into two columns and obtain a clearer and more useful notation +for this function: \[ \text{compute}^{:\bbnum 1+\text{Int}\rightarrow\bbnum 1+\text{Int}}\triangleq\,\begin{array}{|c||cc|} & \bbnum 1 & \text{Int}\\ @@ -398,19 +325,13 @@ \subsection{The nine constructions of fully parametric code} Partial functions are expressed in the matrix notation by writing $\bbnum 0$ in the missing rows: - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0.65\baselineskip} \begin{lstlisting} def get[A]: Option[A] => A = { case Some(x) => x } // Partial function; fails on `None`. \end{lstlisting} -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.35\baselineskip} +Code notation: \[ \text{get}^{:\bbnum 1+A\rightarrow A}\triangleq\,\begin{array}{|c||c|} & A\\ @@ -422,13 +343,9 @@ \subsection{The nine constructions of fully parametric code} A & \text{id} \end{array}\quad. \] -\vspace{-0.9\baselineskip} Scala\textsf{'}s \lstinline!match! expression is equivalent to an application of a disjunctive function: - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} val p: Option[Int] = Some(64) val q: Option[Int] = p match { @@ -437,10 +354,7 @@ \subsection{The nine constructions of fully parametric code} } // The value of q equals Some(32). \end{lstlisting} -\vspace{-2.75\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.85\baselineskip} +Code notation: \[ p\triangleq\bbnum 0^{:\bbnum 1}+64^{:\text{Int}}\quad,\quad q\triangleq p\triangleright\,\begin{array}{|c||cc|} & \bbnum 1 & \text{Int}\\ @@ -448,9 +362,8 @@ \subsection{The nine constructions of fully parametric code} \text{Int} & \bbnum 0 & x\rightarrow\frac{x}{2} \end{array}\quad. \] -\vspace{-0.1\baselineskip} It is convenient to put the argument $p$ to the \emph{left} of the -disjunctive function, as in the Scala code. +disjunctive function, resembling the Scala syntax \lstinline!p match {...}!. Because only one part of a disjunctive type can ever be returned, a row can have at most one non-void value. That value will be in the @@ -550,21 +463,14 @@ \subsection{Function composition and the pipe notation} This book prefers to use the \emph{forward} function composition $(f\bef g$) defined for arbitrary $f^{:A\rightarrow B}$ and $g^{:B\rightarrow C}$ by: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.65\baselineskip} \begin{lstlisting} f andThen g == { x => g(f(x)) } \end{lstlisting} -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.35\baselineskip} +Code notation: \[ f\bef g\triangleq x\rightarrow g(f(x))\quad. \] -\vspace{-0.85\baselineskip} A useful tool for calculations is the \textbf{pipe}\index{pipe notation}\index{\$@$\triangleright$-notation!see \textsf{``}pipe notation\textsf{''}} operation, $x\triangleright f$, which places the argument ($x$) @@ -572,21 +478,13 @@ \subsection{Function composition and the pipe notation} further functions at \emph{right}, for example $(x\triangleright f)\triangleright g$ meaning $g(f(x))$. In Scala, methods such as \lstinline!map! and \lstinline!filter! are often combined in this way: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.65\baselineskip} \begin{lstlisting} x.map(f).filter(p) \end{lstlisting} - -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.35\baselineskip} +Code notation: \[ x\triangleright\text{fmap}\,(f)\triangleright\text{filt}\,(p)\quad. \] -\vspace{-0.85\baselineskip} To enable this common usage, the $\triangleright$ operation is defined to group towards the left. So, the parentheses in $(x\triangleright f)\triangleright g=x\triangleright f\triangleright g$ @@ -597,10 +495,10 @@ \subsection{Function composition and the pipe notation} \[ x\triangleright f\triangleright g=x\triangleright(f\bef g)\quad. \] -Such formulas are needed often, so we follow the convention that the -pipe operation ($\triangleright$) groups weaker than the composition +Such formulas are needed often, so we introduce the convention that +the pipe operation ($\triangleright$) groups weaker than the composition operation ($\bef$).\index{pipe notation!operator precedence} We -can then omit parentheses: $x\triangleright(f\bef g)=x\triangleright f\bef g$. +can then omit more parentheses: $x\triangleright(f\bef g)=x\triangleright f\bef g$. Another common simplification occurs with function compositions of the form: @@ -707,40 +605,26 @@ \subsection{Standard functions for working with products} The \textsf{``}diagonal\textsf{''} function $\Delta$ is a right inverse for $\pi_{1}$ and $\pi_{2}$: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.65\baselineskip} \begin{lstlisting} def delta[A]: A => (A, A) = { x => (x, x) } \end{lstlisting} -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.15\baselineskip} +Code notation: \[ \Delta^{A}:A\rightarrow A\times A\quad,\quad\quad\Delta\triangleq a^{:A}\rightarrow a\times a\quad. \] -\vspace{-1.15\baselineskip} It is clear that extracting any part of a pair \lstinline!delta(x) == (x, x)! will give back the original \lstinline!x!. This property can be written as an equation or a \textsf{``}law\textsf{''}: - -\begin{wrapfigure}{l}{0.5\columnwidth}% -\vspace{-0.65\baselineskip} \begin{lstlisting} delta(x)._1 == x \end{lstlisting} -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.35\baselineskip} +Code notation: \[ \pi_{1}(\Delta(x))=x\quad. \] -\vspace{-0.85\baselineskip} We can transform this law into a point-free equation by first using the pipe notation: @@ -810,18 +694,11 @@ \subsection{Deriving laws for functions with known implementations\label{subsec: type $A\times A$, which is consumed by $f\boxtimes f$. In this way, we see that all types match. We can put the resulting types into a type diagram and write the law with type annotations: - -\begin{wrapfigure}{L}{0.25\columnwidth}% -\vspace{-2\baselineskip} \[ \xymatrix{\xyScaleY{1.6pc}\xyScaleX{4.0pc}A\ar[d]\sb(0.45){f}\ar[r]\sb(0.45){\Delta^{A}} & A\times A\ar[d]\sp(0.45){f\boxtimes f}\\ B\ar[r]\sp(0.45){\Delta^{B}} & B\times B } \] -\vspace{-0.1\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.3\baselineskip} \[ f^{:A\rightarrow B}\bef\Delta^{:B\rightarrow B\times B}=\Delta^{:A\rightarrow A\times A}\bef(f\boxtimes f)\quad. \] @@ -833,9 +710,6 @@ \subsection{Deriving laws for functions with known implementations\label{subsec: We will now perform this computation in the Scala syntax and in the code notation: - -\begin{wrapfigure}{L}{0.54\columnwidth}% -\vspace{-0.6\baselineskip} \begin{lstlisting} x.pipe(f andThen delta) == (f(x)).pipe { a => (a, a) } @@ -844,10 +718,8 @@ \subsection{Deriving laws for functions with known implementations\label{subsec: == (x, x).pipe { case (a, b) => (f(a), f(b)) } == (f(x), f(x)) // Right-hand side. \end{lstlisting} -\vspace{-3\baselineskip} -\end{wrapfigure}% -~\vspace{-1.4\baselineskip} +In the code notation: \begin{align*} & x\triangleright f\bef\Delta=f(x)\,\gunderline{\triangleright\,(b}\rightarrow b\times b)\\ & \quad=f(x)\times f(x)\quad.\\ @@ -855,7 +727,6 @@ \subsection{Deriving laws for functions with known implementations\label{subsec: & \quad=(x\times x)\gunderline{\,\triangleright\,(a\times b}\rightarrow f(a)\times f(b))\\ & \quad=f(x)\times f(x)\quad. \end{align*} -\vspace{-1.5\baselineskip} At each step of the derivation, there is often only one symbolic transformation we can perform. In the example above, each step either substitutes @@ -879,9 +750,6 @@ \subsection{Working with disjunctive types in matrix notation\label{subsec:Worki In many cases, the rules of matrix multiplication and function composition are sufficient for calculating with disjunctive types. For example, consider the following functions \lstinline!swap[A]! and \lstinline!merge[A]!: - -\begin{wrapfigure}{L}{0.46\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} def swap[A]: Either[A, A] => Either[A, A] = { case Left(a) => Right(a) @@ -893,10 +761,7 @@ \subsection{Working with disjunctive types in matrix notation\label{subsec:Worki } \end{lstlisting} -\vspace{-1\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.2\baselineskip} +Code notation: \[ \text{swap}^{A}\triangleq\,\begin{array}{|c||cc|} & A & A\\ @@ -908,10 +773,9 @@ \subsection{Working with disjunctive types in matrix notation\label{subsec:Worki A & \text{id} \end{array}\quad. \] -\vspace{-0.4\baselineskip} We can quickly prove by matrix composition that $\text{swap}\bef\text{swap}=\text{id}$ -and $\text{swap}\bef\text{merge}=\text{merge}$:\vspace{0\baselineskip} +and $\text{swap}\bef\text{merge}=\text{merge}$: \begin{align*} & \text{swap}\bef\text{swap}=\,\begin{array}{||cc|} \bbnum 0 & \text{id}\\ @@ -954,9 +818,6 @@ \subsection{Working with disjunctive types in matrix notation\label{subsec:Worki As another example, consider the function \lstinline!fmap! for the functor $E^{A}\triangleq A+A$: - -\begin{wrapfigure}{L}{0.6\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} def fmap[A, B](f: A => B): Either[A, A] => Either[B, B] = { case Left(a) => Left(f(a)) @@ -964,10 +825,7 @@ \subsection{Working with disjunctive types in matrix notation\label{subsec:Worki } \end{lstlisting} -\vspace{-1.65\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.45\baselineskip} +Code notation: \[ (f^{:A\rightarrow B})^{\uparrow E}\triangleq\,\begin{array}{|c||cc|} & B & B\\ @@ -975,22 +833,14 @@ \subsection{Working with disjunctive types in matrix notation\label{subsec:Worki A & \bbnum 0 & f \end{array}\quad. \] -\vspace{-0.7\baselineskip} With this definition, we can formulate a law of \lstinline!merge! called the \textsf{``}naturality law\index{naturality law!of merge@of \texttt{merge}}\textsf{''}: - -\begin{wrapfigure}{L}{0.3\columnwidth}% -\vspace{-2\baselineskip} \[ \xymatrix{\xyScaleY{1.6pc}\xyScaleX{4.0pc}A+A\ar[d]\sb(0.45){f^{\uparrow E}}\ar[r]\sb(0.55){\text{merge}^{A}} & A\ar[d]\sp(0.45){f}\\ B+B\ar[r]\sp(0.55){\text{merge}^{B}} & B } \] -\vspace{-0.1\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.3\baselineskip} \[ (f^{:A\rightarrow B})^{\uparrow E}\bef\text{merge}^{B}=\text{merge}^{A}\bef f^{:A\rightarrow B}\quad. \] @@ -1030,18 +880,11 @@ \subsection{Working with disjunctive types in matrix notation\label{subsec:Worki Matrix rows and columns can be split or merged when necessary to accommodate various disjunctive types. As an example, let us verify the \textbf{associativity law}\index{associativity law!of merge@of \texttt{merge}} of \lstinline!merge!: - -\begin{wrapfigure}{L}{0.3\columnwidth}% -\vspace{-1.8\baselineskip} \[ \xymatrix{\xyScaleY{1.5pc}\xyScaleX{4.5pc}E^{A+A}\ar[d]\sp(0.45){\text{merge}^{\uparrow E}}\ar[r]\sp(0.55){\text{merge}^{A+A}} & A+A\ar[d]\sb(0.5){\text{merge}^{A}}\\ E^{A}\ar[r]\sb(0.55){\text{merge}^{A}} & A } \] -\vspace{-0.6\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.3\baselineskip} \[ (\text{merge}^{A})^{\uparrow E}\bef\text{merge}^{A}=\text{merge}^{A+A}\bef\text{merge}^{A}\quad. \] @@ -1154,9 +997,6 @@ \subsection{Working with disjunctive types in matrix notation\label{subsec:Worki If all rows of the disjunctive function ignore their arguments and always return the same results, we may collapse all rows into one, as shown in this example: - -\begin{wrapfigure}{L}{0.51\columnwidth}% -\vspace{-0.2\baselineskip} \begin{lstlisting} def same[A]: Either[A, Option[A]] => Option[A] = { case Left(a) => None @@ -1164,23 +1004,19 @@ \subsection{Working with disjunctive types in matrix notation\label{subsec:Worki case Right(Some(a)) => None } \end{lstlisting} -\vspace{-3\baselineskip} -\end{wrapfigure}% -~\vspace{-1.4\baselineskip} -\begin{align*} - & \text{same}^{:A+\bbnum 1+A\rightarrow\bbnum 1+A}\triangleq\,\begin{array}{|c||cc|} +Code notation: +\[ +\text{same}^{:A+\bbnum 1+A\rightarrow\bbnum 1+A}\triangleq\,\begin{array}{|c||cc|} & \bbnum 1 & A\\ \hline A & \_\rightarrow1 & \bbnum 0\\ \bbnum 1 & \_\rightarrow1 & \bbnum 0\\ A & \_\rightarrow1 & \bbnum 0 -\end{array}\\ - & =\,\begin{array}{|c||cc|} +\end{array}=\,\begin{array}{|c||cc|} & \bbnum 1 & A\\ \hline A+\bbnum 1+A & \_\rightarrow1 & \bbnum 0 \end{array}\quad. -\end{align*} -\vspace{-1\baselineskip} +\] There is a more general formula for arbitrary functions $f^{:X\rightarrow C}$ containing this code: @@ -1405,21 +1241,14 @@ \subsubsection{Exercise \label{subsec:Exercise-reasoning-1-6}\ref{subsec:Exercis Now consider a different function \lstinline!swap[A, B]! defined as: - -\begin{wrapfigure}{L}{0.63\columnwidth}% -\vspace{-0.9\baselineskip} \begin{lstlisting} def swap[A, B]: ((A, B)) => (B, A) = { case (a, b) => (b, a) } \end{lstlisting} -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.25\baselineskip} +Code notation: \[ \text{swap}^{A,B}\triangleq a^{:A}\times b^{:B}\rightarrow b\times a\quad. \] -\vspace{-0.15\baselineskip} Show that $\Delta\bef\text{swap}=\Delta$. Write out all types in this law and draw a type diagram. @@ -1438,21 +1267,14 @@ \subsubsection{Exercise \label{subsec:Exercise-reasoning-1-5}\ref{subsec:Exercis Show that the types $(\bbnum 1+\bbnum 1)\times A$ and $A+A$ are equivalent. One direction of this equivalence is given by a function \lstinline!two[A]! with the type signature: - -\begin{wrapfigure}{L}{0.63\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} def two[A]: ((Either[Unit, Unit], A)) => Either[A, A] = ??? \end{lstlisting} -\vspace{-0.25\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.15\baselineskip} +Code notation: \[ \text{two}^{A}:(\bbnum 1+\bbnum 1)\times A\rightarrow A+A\quad. \] -\vspace{-0.35\baselineskip} Implement that function and prove that it satisfies the \textsf{``}naturality law\textsf{''}: for any $f^{:A\rightarrow B}$, \[ diff --git a/sofp-src/sofp-summary.lyx b/sofp-src/sofp-summary.lyx index 3add754ca..910545c78 100644 --- a/sofp-src/sofp-summary.lyx +++ b/sofp-src/sofp-summary.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -2194,52 +2209,6 @@ def filter[A](p: A => Boolean)(s: Seq[A]): Seq[A] = s.flatMap { a => ??? \end_inset -\end_layout - -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "par:Exercise-additional-4" - -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "par:Exercise-additional-4" -plural "false" -caps "false" -noprefix "false" - -\end_inset - - -\end_layout - -\begin_layout Standard -(Bird-de Moor, page 20) Derive the following identity between functions - -\begin_inset Formula $F^{A}\rightarrow F^{A}$ -\end_inset - -, for any filterable functor -\begin_inset Formula $F$ -\end_inset - - and any predicate -\begin_inset Formula $p^{:A\rightarrow\bbnum 2}$ -\end_inset - -: -\begin_inset Formula -\[ -\text{filt}_{F}(p)=(\Delta\bef\text{id}\boxtimes p)^{\uparrow F}\bef\text{filt}_{F}(\pi_{2})\bef\pi_{1}^{\uparrow F}\quad. -\] - -\end_inset - - \end_layout \begin_layout Subsubsection @@ -2334,12 +2303,109 @@ empty \begin_layout Plain Layout -p1 |+| p2 // Must be the same as the concatenation of all `case` clauses. +val p3 = p1 |+| p2 // Must be the same as the concatenation of all `case` + clauses. \end_layout \end_inset +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "par:Exercise-additional-6-1-1" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "par:Exercise-additional-6-1-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +Find +\begin_inset Foot +status collapsed + +\begin_layout Plain Layout +This was posted in +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "https://cstheory.stackexchange.com/questions/53294" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + a general type signature for the expression +\begin_inset Formula $a\rightarrow a(y\rightarrow t\rightarrow t)(z(a))$ +\end_inset + +. + +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "par:Exercise-additional-4" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "par:Exercise-additional-4" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +(Bird-de Moor, page 20) Derive the following identity between functions + +\begin_inset Formula $F^{A}\rightarrow F^{A}$ +\end_inset + +, for any filterable functor +\begin_inset Formula $F$ +\end_inset + + and any predicate +\begin_inset Formula $p^{:A\rightarrow\bbnum 2}$ +\end_inset + +: +\begin_inset Formula +\[ +\text{filt}_{F}(p)=(\Delta\bef\text{id}\boxtimes p)^{\uparrow F}\bef\text{filt}_{F}(\pi_{2})\bef\pi_{1}^{\uparrow F}\quad. +\] + +\end_inset + + \end_layout \begin_layout Subsubsection @@ -2462,6 +2528,78 @@ Show that all polynomial functors do not belong to this typeclass. \end_layout +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "par:Exercise-additional-6-1" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "par:Exercise-additional-6-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +Consider the method +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +sequence: L[F[A]] => F[L[A]] +\end_layout + +\end_inset + + (denoted by +\begin_inset Formula $\text{seq}_{L}^{F}$ +\end_inset + +) that assumes a traversable functor +\begin_inset Formula $L$ +\end_inset + + and an applicative functor +\begin_inset Formula $F$ +\end_inset + +. + If we set +\begin_inset Formula $F=\text{List}$ +\end_inset + + and also +\begin_inset Formula $L=\text{List}$ +\end_inset + + then we obtain the type signature +\begin_inset Formula $\text{seq}_{\text{List}}^{\text{List}}:\text{List}^{\text{List}^{A}}\rightarrow\text{List}^{\text{List}^{A}}$ +\end_inset + +. + A data structure of type +\begin_inset Formula $\text{List}^{\text{List}^{A}}$ +\end_inset + + may be used to represent a rectangular matrix. + Show that the function +\begin_inset Formula $\text{seq}_{\text{List}}^{\text{List}}$ +\end_inset + + transposes the rectangular matrix. +\end_layout + \begin_layout Subsubsection Exercise \begin_inset CommandInset label @@ -4684,7 +4822,11 @@ noprefix "false" \end_layout \begin_layout Standard -Show that the applicative naturality law + +\series bold +(a) +\series default + Show that the applicative naturality law \begin_inset space ~ \end_inset @@ -4698,7 +4840,21 @@ noprefix "false" \end_inset -) and the composition law +), the identity law +\begin_inset space ~ +\end_inset + +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:traverse-identity-law" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +), and the composition law \begin_inset space ~ \end_inset @@ -4740,7 +4896,8 @@ traverse \begin_inset Formula $F$ \end_inset --effect exactly once. +-effect exactly once, or find another law that is necessary to guarantee + that. (Can we use a composition of an applicative functor \begin_inset Formula $F$ \end_inset @@ -4764,6 +4921,183 @@ State -effects were collected?) \end_layout +\begin_layout Standard + +\series bold +(b) +\series default + For any lawful traversable functor +\begin_inset Formula $L$ +\end_inset + +, define the function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + (denoted for brevity by +\begin_inset Formula $\text{zwi}_{L}$ +\end_inset + +) via Eq. +\begin_inset space ~ +\end_inset + +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:definition-of-zwi" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). + Show that there exists a +\begin_inset Quotes eld +\end_inset + +tabulating +\begin_inset Quotes erd +\end_inset + + function (denoted by +\begin_inset Formula $\text{tab}_{L}^{A}$ +\end_inset + +): +\begin_inset Formula +\[ +\text{tab}_{L}^{A}:L^{A\times\text{Int}}\rightarrow(\text{Int}\rightarrow A)\times L^{\text{Int}}\quad, +\] + +\end_inset + +such that +\begin_inset Formula $\text{zwi}_{L}\bef\text{tab}_{L}\bef(f^{:\text{Int}\rightarrow A}\times q^{:L^{\text{Int}}}\rightarrow q\triangleright f^{\uparrow L})=\text{id}^{:L^{A}\rightarrow L^{A}}$ +\end_inset + +. + So, the function +\begin_inset Formula $\text{zwi}_{L}\bef\text{tab}_{L}$ +\end_inset + + is an injective map of type +\begin_inset Formula $L^{A}\rightarrow(\text{Int}\rightarrow A)\times L^{\text{Int}}$ +\end_inset + +. +\end_layout + +\begin_layout Standard +It appears that this property +\emph on +cannot +\emph default + be proved via the three laws of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +traverse +\end_layout + +\end_inset + +. + One reason is that the function +\begin_inset Formula $\text{tab}_{L}$ +\end_inset + + produces an +\begin_inset Quotes eld +\end_inset + +indexing +\begin_inset Quotes erd +\end_inset + + function +\begin_inset Formula $f:\text{Int}\rightarrow A$ +\end_inset + +, which can be computed only by traversing the entire data structure +\begin_inset Formula $L^{A}$ +\end_inset + +. + In other words, +\begin_inset Formula $f$ +\end_inset + + is a result of a traversal operation. + We want to prove a property of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +traverse +\end_layout + +\end_inset + + that combines +\begin_inset Formula $f$ +\end_inset + + withanother traversal ( +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + +). + But there are no laws of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +traverse +\end_layout + +\end_inset + + that involve reusing a result of another traverse operation. + What new law of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +traverse +\end_layout + +\end_inset + + is necessary? +\end_layout + \begin_layout Subsubsection Problem \begin_inset CommandInset label diff --git a/sofp-src/sofp-summary.tex b/sofp-src/sofp-summary.tex index ceeafdf97..134b51c02 100644 --- a/sofp-src/sofp-summary.tex +++ b/sofp-src/sofp-summary.tex @@ -315,16 +315,6 @@ \subsubsection{Exercise \label{par:Exercise-additional-3}\ref{par:Exercise-addit \end{lstlisting} -\subsubsection{Exercise \label{par:Exercise-additional-4}\ref{par:Exercise-additional-4}} - -(Bird-de Moor, page 20) Derive the following identity between functions -$F^{A}\rightarrow F^{A}$, for any filterable functor $F$ and any -predicate $p^{:A\rightarrow\bbnum 2}$: -\[ -\text{filt}_{F}(p)=(\Delta\bef\text{id}\boxtimes p)^{\uparrow F}\bef\text{filt}_{F}(\pi_{2})\bef\pi_{1}^{\uparrow F}\quad. -\] - - \subsubsection{Exercise \label{par:Exercise-additional-5}\ref{par:Exercise-additional-5}} Define a monoid of partial functions with fixed types $P\rightarrow Q$: @@ -336,10 +326,25 @@ \subsubsection{Exercise \label{par:Exercise-additional-5}\ref{par:Exercise-addit case Some(20) => "twenty" case None => "empty" } -p1 |+| p2 // Must be the same as the concatenation of all `case` clauses. +val p3 = p1 |+| p2 // Must be the same as the concatenation of all `case` clauses. \end{lstlisting} +\subsubsection{Exercise \label{par:Exercise-additional-6-1-1}\ref{par:Exercise-additional-6-1-1}} + +Find\footnote{This was posted in \texttt{\href{https://cstheory.stackexchange.com/questions/53294}{https://cstheory.stackexchange.com/questions/53294}}} +a general type signature for the expression $a\rightarrow a(y\rightarrow t\rightarrow t)(z(a))$. + +\subsubsection{Exercise \label{par:Exercise-additional-4}\ref{par:Exercise-additional-4}} + +(Bird-de Moor, page 20) Derive the following identity between functions +$F^{A}\rightarrow F^{A}$, for any filterable functor $F$ and any +predicate $p^{:A\rightarrow\bbnum 2}$: +\[ +\text{filt}_{F}(p)=(\Delta\bef\text{id}\boxtimes p)^{\uparrow F}\bef\text{filt}_{F}(\pi_{2})\bef\pi_{1}^{\uparrow F}\quad. +\] + + \subsubsection{Exercise \label{par:Exercise-additional-6}\ref{par:Exercise-additional-6}} Consider a typeclass called \textsf{``}\lstinline!Splittable!\textsf{''} for functors\index{splittable functors} @@ -365,6 +370,16 @@ \subsubsection{Exercise \label{par:Exercise-additional-6}\ref{par:Exercise-addit Show that exponential functors such as $F^{A}\triangleq Z\rightarrow A$ do not belong to this typeclass. +\subsubsection{Exercise \label{par:Exercise-additional-6-1}\ref{par:Exercise-additional-6-1}} + +Consider the method \lstinline!sequence: L[F[A]] => F[L[A]]! (denoted +by $\text{seq}_{L}^{F}$) that assumes a traversable functor $L$ +and an applicative functor $F$. If we set $F=\text{List}$ and also +$L=\text{List}$ then we obtain the type signature $\text{seq}_{\text{List}}^{\text{List}}:\text{List}^{\text{List}^{A}}\rightarrow\text{List}^{\text{List}^{A}}$. +A data structure of type $\text{List}^{\text{List}^{A}}$ may be used +to represent a rectangular matrix. Show that the function $\text{seq}_{\text{List}}^{\text{List}}$ +transposes the rectangular matrix. + \subsubsection{Exercise \label{par:Exercise-additional-7}\ref{par:Exercise-additional-7}} Given a monad $M$ and a fixed type $Z$, consider the functor $F^{A}\triangleq(A\rightarrow M^{Z})\rightarrow Z$. @@ -640,12 +655,35 @@ \subsubsection{Problem \label{subsec:Problem-co-pointed-applicative}\ref{subsec: \subsubsection{Problem \label{par:Problem-traverse-law}\ref{par:Problem-traverse-law}} -Show that the applicative naturality law~(\ref{eq:traverse-applicative-naturality-law}) -and the composition law~(\ref{eq:composition-law-of-traverse}) of -\lstinline!traverse! guarantee that \lstinline!traverse! collects -each $F$-effect exactly once. (Can we use a composition of an applicative -functor $F$ with a \lstinline!State! monad to count the number of -times $F$-effects were collected?) +\textbf{(a)} Show that the applicative naturality law~(\ref{eq:traverse-applicative-naturality-law}), +the identity law~(\ref{eq:traverse-identity-law}), and the composition +law~(\ref{eq:composition-law-of-traverse}) of \lstinline!traverse! +guarantee that \lstinline!traverse! collects each $F$-effect exactly +once, or find another law that is necessary to guarantee that. (Can +we use a composition of an applicative functor $F$ with a \lstinline!State! +monad to count the number of times $F$-effects were collected?) + +\textbf{(b)} For any lawful traversable functor $L$, define the function +\lstinline!zipWithIndex! (denoted for brevity by $\text{zwi}_{L}$) +via Eq.~(\ref{eq:definition-of-zwi}). Show that there exists a \textsf{``}tabulating\textsf{''} +function (denoted by $\text{tab}_{L}^{A}$): +\[ +\text{tab}_{L}^{A}:L^{A\times\text{Int}}\rightarrow(\text{Int}\rightarrow A)\times L^{\text{Int}}\quad, +\] +such that $\text{zwi}_{L}\bef\text{tab}_{L}\bef(f^{:\text{Int}\rightarrow A}\times q^{:L^{\text{Int}}}\rightarrow q\triangleright f^{\uparrow L})=\text{id}^{:L^{A}\rightarrow L^{A}}$. +So, the function $\text{zwi}_{L}\bef\text{tab}_{L}$ is an injective +map of type $L^{A}\rightarrow(\text{Int}\rightarrow A)\times L^{\text{Int}}$. + +It appears that this property \emph{cannot} be proved via the three +laws of \lstinline!traverse!. One reason is that the function $\text{tab}_{L}$ +produces an \textsf{``}indexing\textsf{''} function $f:\text{Int}\rightarrow A$, +which can be computed only by traversing the entire data structure +$L^{A}$. In other words, $f$ is a result of a traversal operation. +We want to prove a property of \lstinline!traverse! that combines +$f$ withanother traversal (\lstinline!zipWithIndex!). But there +are no laws of \lstinline!traverse! that involve reusing a result +of another traverse operation. What new law of \lstinline!traverse! +is necessary? \subsubsection{Problem \label{par:Problem-monads-2}\ref{par:Problem-monads-2}} diff --git a/sofp-src/sofp-transformers.lyx b/sofp-src/sofp-transformers.lyx index 321e72802..7d6ffd5ab 100644 --- a/sofp-src/sofp-transformers.lyx +++ b/sofp-src/sofp-transformers.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -1241,7 +1256,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -8869,7 +8884,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -9439,7 +9454,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -12049,7 +12064,7 @@ up[MyStack] \end_layout \begin_layout Subsection -Constructing lifts via typeclasses for monad operations ( +Constructing lifts via operation typeclasses ( \begin_inset Quotes eld \end_inset @@ -42759,7 +42774,8 @@ We now turn to the monadic morphism laws. \end_layout \begin_layout Standard -The composition law is quicker to check in the Kleisli formulation: +The composition law is quicker to work with in the Kleisli formulation: + \begin_inset Formula \[ \big(f^{:A\rightarrow S\rightarrow M^{B\times S}}\bef\text{brun}\,(s_{0})\big)\diamond_{_{M}}\big(g^{:B\rightarrow S\rightarrow M^{C\times S}}\bef\text{brun}\,(s_{0})\big)\overset{?}{=}(f\diamond_{_{P}}g)\bef\text{brun}\,(s_{0})\quad. @@ -49257,7 +49273,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -56108,7 +56124,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset diff --git a/sofp-src/sofp-transformers.tex b/sofp-src/sofp-transformers.tex index a9e66b9e8..af233abb1 100644 --- a/sofp-src/sofp-transformers.tex +++ b/sofp-src/sofp-transformers.tex @@ -151,7 +151,7 @@ \subsection{Combining monadic effects via functor composition} prove that in Section~\ref{sec:transformers-linear-monads}), but not necessarily on the outside. -\subsubsection{Example \label{subsec:Example-either-reader-not-compose}\ref{subsec:Example-either-reader-not-compose}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-either-reader-not-compose}\ref{subsec:Example-either-reader-not-compose}\index{examples (with code)}} Show that a \lstinline!Reader! monad does not compose inside of an \lstinline!Either! monad. @@ -1109,7 +1109,7 @@ \subsection{Combining more than two monads: monad stacks\label{subsec:Combining- \end{table} -\subsubsection{Example \label{subsec:Example-reader-state-transformed}\ref{subsec:Example-reader-state-transformed}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-reader-state-transformed}\ref{subsec:Example-reader-state-transformed}\index{examples (with code)}} Show that $T_{\text{State}}^{\text{Reader}}$ is equivalent to that of $T_{\text{Reader}}^{\text{State}}$. @@ -1225,7 +1225,7 @@ \subsubsection{Example \label{subsec:Example-either-state-transformed-cancels-ef A monad stack is then denoted unambiguously by, e.g., $K\varangle L\varangle M\varangle N$, without need for parentheses. -\subsubsection{Example \label{subsec:Example-monad-stack}\ref{subsec:Example-monad-stack}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-monad-stack}\ref{subsec:Example-monad-stack}\index{examples (with code)}} Define type constructors for these monad stacks and for their transformers: @@ -1632,7 +1632,7 @@ \subsection{Constructing lifts via type relations\label{subsec:Constructing-lift and use the \lstinline!up[MyStack]! method as an automatic lift from any level within the stack to the top of the stack. -\subsection{Constructing lifts via typeclasses for monad operations (\textquotedblleft MTL-style\textquotedblright )\label{subsec:Combining-monads-via-mtl-style}} +\subsection{Constructing lifts via operation typeclasses (\textquotedblleft MTL-style\textquotedblright )\label{subsec:Combining-monads-via-mtl-style}} The MTL-style\index{MTL-style monadic programs}\footnote{The name MTL stands for \textsf{``}monad transformer library\textsf{''}.} monadic\index{monadic program!MTL-style} programming is another way @@ -8755,7 +8755,7 @@ \subsubsection{Statement \label{subsec:Statement-state-monad-transformer}\ref{su {\color{greenunder}\text{naturality law of }\text{pu}_{M}:}\quad & =a\rightarrow(a\times s_{0})\triangleright\pi_{1}\bef\text{pu}_{M}=a\rightarrow a\triangleright\text{pu}_{M}=\text{pu}_{M}\quad. \end{align*} -The composition law is quicker to check in the Kleisli formulation: +The composition law is quicker to work with in the Kleisli formulation: \[ \big(f^{:A\rightarrow S\rightarrow M^{B\times S}}\bef\text{brun}\,(s_{0})\big)\diamond_{_{M}}\big(g^{:B\rightarrow S\rightarrow M^{C\times S}}\bef\text{brun}\,(s_{0})\big)\overset{?}{=}(f\diamond_{_{P}}g)\bef\text{brun}\,(s_{0})\quad. \] @@ -10069,7 +10069,7 @@ \subsubsection{Statement \label{subsec:Statement-rigid-monads-are-rigid-functors \] -\subsubsection{Example \label{subsec:Example-fo-fi-not-id}\ref{subsec:Example-fo-fi-not-id}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-fo-fi-not-id}\ref{subsec:Example-fo-fi-not-id}\index{examples (with code)}} Show that the functions \lstinline!fuseIn! and \lstinline!fuseOut! are not always inverses: for some $R$, @@ -11169,7 +11169,7 @@ \subsubsection{Statement \label{subsec:Statement-monatron-not-natural-lifting-li \lstinline!clear! operation to any stack $Q\triangleq M\varangle N=N\circ M$ like this: -\subsubsection{Example \label{subsec:Statement-monatron-lifting-writer-clear-to-base}\ref{subsec:Statement-monatron-lifting-writer-clear-to-base}\index{solved examples}} +\subsubsection{Example \label{subsec:Statement-monatron-lifting-writer-clear-to-base}\ref{subsec:Statement-monatron-lifting-writer-clear-to-base}\index{examples (with code)}} For any monad $N$, define $Q\triangleq\text{Writer}^{W,\bullet}\varangle N$, so $Q^{A}\triangleq N^{A\times W}$. A monadically natural lifting diff --git a/sofp-src/sofp-traversable.lyx b/sofp-src/sofp-traversable.lyx index ba5f1d2db..18832c275 100644 --- a/sofp-src/sofp-traversable.lyx +++ b/sofp-src/sofp-traversable.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -1538,10 +1553,6 @@ traverse \end_inset : -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset Formula \[ \text{traverse}:(A\rightarrow\text{Future}^{B})\rightarrow\text{List}^{A}\rightarrow\text{Future}^{\text{List}^{B}}\quad. @@ -1890,7 +1901,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -4655,6 +4666,13 @@ traverse Decorating a tree. I. Depth-first traversal +\begin_inset CommandInset label +LatexCommand label +name "subsec:Decorating-a-tree1" + +\end_inset + + \end_layout \begin_layout Standard @@ -7635,10 +7653,6 @@ Traversable Implementing \family typewriter scanLeft -\family default - via -\family typewriter -traverse \end_layout \begin_layout Standard @@ -10689,7 +10703,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -11651,56 +11665,7 @@ fullBinaryTree \end_inset . - To test the resulting code, compute a full tree -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "74col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -scala> fullBinaryTree(2) -\end_layout - -\begin_layout Plain Layout - -res0: T2[Int] = Branch(Branch(Leaf(0), Leaf(1)), Branch(Leaf(2), Leaf(3))) -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace 50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -of depth + To test the resulting code, compute a full tree of depth \begin_inset Formula $2$ \end_inset @@ -11732,11 +11697,20 @@ Tree[ [ 0 1 ] [ 2 3 ] ] \end_inset -\begin_inset Newline newline -\end_inset +\begin_inset listings +inline false +status open +\begin_layout Plain Layout + +scala> fullBinaryTree(2) +\end_layout + +\begin_layout Plain Layout + +res0: T2[Int] = Branch(Branch(Leaf(0), Leaf(1)), Branch(Leaf(2), Leaf(3))) +\end_layout -\begin_inset space ~ \end_inset @@ -13089,7 +13063,7 @@ tree1toInf \end_inset - is finite but can compute on demand a potentially unbounded number of leaves. + is finite but can compute a tree of unbounded depth. To visualize \begin_inset listings inline true @@ -13656,7 +13630,11 @@ zipWithDepth \end_inset - via a traversal with a binary tree recursion scheme. + via +\begin_inset Formula $\text{trav}_{S}$ +\end_inset + + with a binary tree recursion scheme. We will use the \begin_inset listings inline true @@ -14275,7 +14253,19 @@ Traversable right-to-left \emph default depth-first traversal order. - Use that to implement + Use that +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Traversable +\end_layout + +\end_inset + + instance to implement \begin_inset listings inline true status open @@ -15329,18 +15319,11 @@ reduceE \end_inset is then formulated as: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "20col%" -status open +\begin_inset Formula +\begin{equation} +\text{reduceE}^{M}\bef f^{:M\rightarrow N}=f^{\uparrow L}\bef\text{reduceE}^{N}\quad.\label{eq:monoidal-naturality-law-of-reduceE} +\end{equation} -\begin_layout Plain Layout -\begin_inset VSpace -215baselineskip% \end_inset @@ -15361,34 +15344,6 @@ L^{N}\ar[r]\sp(0.5){~\text{reduceE}^{N}} & N \end_inset - -\begin_inset VSpace -60baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -40baselineskip% -\end_inset - - -\begin_inset Formula -\begin{equation} -\text{reduceE}^{M}\bef f^{:M\rightarrow N}=f^{\uparrow L}\bef\text{reduceE}^{N}\quad.\label{eq:monoidal-naturality-law-of-reduceE} -\end{equation} - -\end_inset - Here the types \begin_inset Formula $M$ \end_inset @@ -17805,10 +17760,6 @@ The last equation can be applied to any function \end_inset : -\begin_inset VSpace -30baselineskip% -\end_inset - - \begin_inset Formula \[ \phi(p)(k(a_{0}))\overset{!}{=}\phi(p\triangleright\text{inF}^{\uparrow L})(k)(a_{0})\quad. @@ -17844,10 +17795,6 @@ noprefix "false" \end_inset in the line above: -\begin_inset VSpace -30baselineskip% -\end_inset - - \begin_inset Formula \[ \phi(p)(a_{0})\overset{!}{=}\phi(p\triangleright\text{inF}^{\uparrow L})(\text{id})(a_{0})\quad. @@ -18031,7 +17978,7 @@ noprefix "false" ) holds, and we may use its conclusion: \begin_inset Formula \[ -f\bef\text{foldFn}\,(x)\overset{!}{=}\text{foldFn}(y)\bef f\quad\text{or equivalently}:\quad\text{foldFn}\,(p\triangleright\text{inMF}^{\uparrow L})\bef f=f\bef\text{foldFn}(p\triangleright f^{\uparrow L}\bef\text{inMF}^{\uparrow L}). +f\bef\text{foldFn}\,(x)\overset{!}{=}\text{foldFn}(y)\bef f\quad\text{or equivalently:}\quad\text{foldFn}\,(p\triangleright\text{inMF}^{\uparrow L})\bef f=f\bef\text{foldFn}(p\triangleright f^{\uparrow L}\bef\text{inMF}^{\uparrow L}). \] \end_inset @@ -19692,7 +19639,15 @@ List \end_inset - is a recursively defined polynomial functor + is a +\emph on +recursively +\emph default + +\emph on +defined +\emph default + polynomial functor \begin_inset Index idx status open @@ -19703,8 +19658,8 @@ polynomial functor!recursive \end_inset ). - These functors represent containers that store a finite number of values - of type + Those functors represent data structures that store a finite number of + values of type \begin_inset listings inline true status open @@ -19717,7 +19672,7 @@ A \end_inset . - So, it is clear what it means to extract + It is clear what it means to extract \begin_inset Quotes eld \end_inset @@ -19729,7 +19684,7 @@ all values of type \begin_inset Quotes erd \end_inset - from such containers. + from such data structures. To show that all polynomial functors are foldable, we will define \begin_inset listings inline true @@ -19742,7 +19697,7 @@ toList \end_inset - inductively via structural analysis. + inductively via structural analysis of functor types. The definition will ensure that \begin_inset listings inline true @@ -19755,7 +19710,7 @@ toList \end_inset - extracts exactly as many values as stored in the polynomial functor. + correctly extracts the values stored by a given data structure. \end_layout \begin_layout Standard @@ -21422,10 +21377,6 @@ noprefix "false" \end_inset -\begin_inset VSpace -120baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{2.0pc}\xyScaleX{5.0pc}L^{X}\ar[rd]\sp(0.6){~~\text{trav}_{L}^{F,X,B}(f\bef g)}\ar[d]\sp(0.45){f^{\uparrow L}} & & L^{A}\ar[rd]\sb(0.35){\text{trav}_{L}^{F,A,C}(g\bef h)~~}\ar[r]\sp(0.5){\text{trav}_{L}^{F,A,B}(g)} & F^{L^{B}}\ar[d]\sp(0.45){h^{\uparrow L\uparrow F}}\\ @@ -21518,7 +21469,7 @@ zip \end_inset ). - To formulate this + To formulate this requirement as an \series bold applicative naturality law \series default @@ -21543,26 +21494,15 @@ traverse \begin_inset Formula $F$ \end_inset - to another arbitrary applicative functor + to another +\emph on +arbitrary +\emph default + applicative functor \begin_inset Formula $G$ \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "26col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -130baselineskip% -\end_inset - - \begin_inset Formula $\xymatrix{\xyScaleY{1.7pc}\xyScaleX{5.0pc}L^{A}\ar[rd]\sb(0.4){\text{trav}_{L}^{G,A,B}(g\bef f)~~}\ar[r]\sp(0.5){\text{trav}_{L}^{F,A,B}(g)} & F^{L^{B}}\ar[d]\sp(0.45){f}\\ & G^{L^{B}} } @@ -21570,29 +21510,6 @@ $ \end_inset -\begin_inset VSpace 50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\begin_layout Standard \begin_inset Formula \begin{equation} \text{trav}_{L}^{G,A,B}(g^{:A\rightarrow F^{B}}\bef f^{:F^{B}\rightarrow G^{B}})=\text{trav}_{L}^{F,A,B}(g)\bef f^{:F^{L^{B}}\rightarrow G^{L^{B}}}\quad.\label{eq:traverse-applicative-naturality-law} @@ -21600,11 +21517,6 @@ $ \end_inset - -\end_layout - -\begin_layout Standard -\noindent Here \begin_inset Formula $f$ \end_inset @@ -21634,16 +21546,15 @@ Here \end_layout \begin_layout Standard -In addition to being a natural transformation, the function +Another requirement for the function \begin_inset Formula $f$ \end_inset - must preserve the applicative typeclass. - In other words, + is that \begin_inset Formula $f$ \end_inset - should map + must map \begin_inset Formula $F$ \end_inset @@ -21819,7 +21730,7 @@ noprefix "false" status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -22534,9 +22445,6 @@ To verify the composition law: \end_inset -\end_layout - -\begin_layout Standard \begin_inset Formula $\square$ \end_inset @@ -22841,7 +22749,7 @@ traverse : \begin_inset Formula \begin{equation} -\text{trav}_{L}^{\text{Id},A,A}(\text{id})=\text{id}\quad.\label{eq:traverse-identity-law} +\text{trav}_{L}^{\text{Id},A,A}(\text{id}^{:A\rightarrow A})=\text{id}^{:L^{A}\rightarrow L^{A}}\quad.\label{eq:traverse-identity-law} \end{equation} \end_inset @@ -23344,10 +23252,6 @@ traverse \end_inset , and compute the following composition: -\begin_inset VSpace -15baselineskip% -\end_inset - - \begin_inset Formula \[ p\triangleright\text{trav}_{L}^{F,A,B}(f)\triangleright\big(\text{trav}_{L}^{G,B,C}(g)\big)^{\uparrow F}:F^{G^{L^{C}}}\quad. @@ -23356,10 +23260,6 @@ p\triangleright\text{trav}_{L}^{F,A,B}(f)\triangleright\big(\text{trav}_{L}^{G,B \end_inset -\begin_inset VSpace -65baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -23442,10 +23342,6 @@ traverse \end_inset operation using that functor and obtain: -\begin_inset VSpace -50baselineskip% -\end_inset - - \begin_inset Formula \[ p\triangleright\text{trav}_{L}^{F\circ G,A,C}(f\bef g^{\uparrow F}):F^{G^{L^{C}}}\quad. @@ -23454,41 +23350,12 @@ p\triangleright\text{trav}_{L}^{F\circ G,A,C}(f\bef g^{\uparrow F}):F^{G^{L^{C}} \end_inset -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "38col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -160baselineskip% -\end_inset - - \begin_inset Formula $\xymatrix{\xyScaleY{1.5pc}\xyScaleX{3.4pc}L^{A}\ar[dr]\sb(0.35){\text{trav}_{L}^{F\circ G,A,C}(f\bef g^{\uparrow F})~~}\ar[r]\sp(0.5){\text{trav}_{L}^{F,A,B}(f)} & F^{L^{B}}\ar[d]\sp(0.4){\big(\text{trav}_{L}^{G,B,C}(g)\big)^{\uparrow F}}\\ & F^{G^{L^{C}}} } $ \end_inset - -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The \series bold composition law @@ -23520,7 +23387,7 @@ traverse says that the two results should be equal: \begin_inset Formula \begin{equation} -\negthickspace\negthickspace\negthickspace\negthickspace\text{trav}_{L}^{F,A,B}(f)\bef\big(\text{trav}_{L}^{G,B,C}(g)\big)^{\uparrow F}=\text{trav}_{L}^{F\circ G,A,C}(f\bef g^{\uparrow F})\quad.\label{eq:composition-law-of-traverse} +\text{trav}_{L}^{F,A,B}(f)\bef\big(\text{trav}_{L}^{G,B,C}(g)\big)^{\uparrow F}=\text{trav}_{L}^{F\circ G,A,C}(f\bef g^{\uparrow F})\quad.\label{eq:composition-law-of-traverse} \end{equation} \end_inset @@ -24183,39 +24050,39 @@ traverse \end_inset are excluded by the composition laws. - But it remains unknown (Problem -\begin_inset space ~ + But it remains unknown whether the known three laws always guarantee that + the traverse function collects each +\begin_inset Formula $F$ \end_inset +-effect exactly once. + Perhaps another new law of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "par:Problem-traverse-law" -plural "false" -caps "false" -noprefix "false" +traverse +\end_layout \end_inset -) whether the composition law + is required (see Problem \begin_inset space ~ \end_inset -( + \begin_inset CommandInset ref LatexCommand ref -reference "eq:composition-law-of-traverse" +reference "par:Problem-traverse-law" plural "false" caps "false" noprefix "false" \end_inset -) always guarantees that the traverse function collects each -\begin_inset Formula $F$ -\end_inset - --effect exactly once. +). \end_layout \begin_layout Subsection @@ -24296,31 +24163,9 @@ sequence \end_inset : -\begin_inset Note Comment -status open - -\begin_layout Plain Layout -precarious formatting -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement i -overhang 0in -width "22col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -130baselineskip% -\end_inset - - \begin_inset Formula $\xymatrix{\xyScaleY{1.5pc}\xyScaleX{2.0pc}L^{F^{A}}\ar[d]\sb(0.45){\text{seq}_{L}^{F,A}}\ar[r]\sb(0.5){f^{\uparrow F\uparrow L}} & L^{F^{B}}\ar[d]\sp(0.45){\text{seq}_{L}^{F,B}}\\ F^{L^{A}}\ar[r]\sp(0.5){f^{\uparrow L\uparrow F}} & F^{L^{B}} } @@ -24328,29 +24173,6 @@ $ \end_inset -\begin_inset VSpace 150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset space ~ -\end_inset - - -\begin_inset VSpace -140baselineskip% -\end_inset - - -\end_layout - -\begin_layout Standard \begin_inset Formula \begin{equation} (f^{:A\rightarrow B})^{\uparrow F\uparrow L}\bef\text{seq}_{L}^{F,B}=\text{seq}_{L}^{F,A}\bef f^{\uparrow L\uparrow F}\quad.\label{eq:sequence-naturality-law} @@ -24358,11 +24180,6 @@ $ \end_inset - -\end_layout - -\begin_layout Standard -\noindent It will be shown in Exercise \begin_inset space ~ \end_inset @@ -24501,38 +24318,12 @@ noprefix "false" \end_layout \begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "22col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -110baselineskip% -\end_inset - - \begin_inset Formula $\xymatrix{\xyScaleY{1.5pc}\xyScaleX{2.0pc}L^{F^{A}}\ar[d]\sb(0.45){\text{seq}_{L}^{F,A}}\ar[r]\sb(0.5){(f^{A})^{\uparrow L}} & L^{G^{A}}\ar[d]\sp(0.45){\text{seq}_{L}^{G,A}}\\ F^{L^{A}}\ar[r]\sp(0.5){f^{L^{A}}} & G^{L^{A}} } $ \end_inset - -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Renaming \begin_inset Formula $B$ \end_inset @@ -24738,41 +24529,12 @@ sequence \end_inset -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "30col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -170baselineskip% -\end_inset - - \begin_inset Formula $\xymatrix{\xyScaleY{1.4pc}\xyScaleX{3.0pc}L^{F^{G^{A}}}\ar[r]\sp(0.55){\text{seq}_{L}^{F,G^{A}}}\ar[rd]\sb(0.4){\text{seq}_{L}^{F\circ G,A}} & F^{L^{G^{A}}}\ar[d]\sp(0.4){(\text{seq}_{L}^{G,A})^{\uparrow F}}\\ & F^{G^{L^{A}}} } $ \end_inset - -\begin_inset VSpace -190baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Omitting the common function \begin_inset Formula $(f\bef g^{\uparrow F})^{\uparrow L}$ \end_inset @@ -25315,7 +25077,11 @@ N^{F^{A}} & \text{seq}_{N}^{F,A}\bef(n^{:N^{A}}\rightarrow\bbnum 0^{:M^{A}}+n)^{ \begin_layout Standard \begin_inset Note Comment -status collapsed +status open + +\begin_layout Plain Layout +*** do we need this? +\end_layout \begin_layout Plain Layout It helps to rewrite @@ -26018,56 +25784,17 @@ Traversable \end_layout \begin_layout Standard -Non-polynomial functors are, in general, not traversable. - As an example, consider the functors -\begin_inset Formula $L^{A}\triangleq E\rightarrow A$ -\end_inset - - and -\begin_inset Formula $F^{A}\triangleq Z+A$ -\end_inset - -, where the types -\begin_inset Formula $E$ -\end_inset - - and -\begin_inset Formula $Z$ -\end_inset - - are arbitrary but fixed. - It is impossible to implement a fully parametric function with the type - signature of -\begin_inset listings -inline true -status open +Functors that are not equivalent to polynomial functors are not traversable. +\begin_inset Foot +status collapsed \begin_layout Plain Layout - -sequence -\end_layout - -\end_inset - - ( -\begin_inset Formula $L^{F^{B}}\rightarrow F^{L^{B}}$ -\end_inset - -), which is -\begin_inset Formula $(E\rightarrow Z+A)\rightarrow Z+(E\rightarrow A)$ -\end_inset - -. - To prove that any traversable functor +To prove that any traversable functor \emph on must \emph default be polynomial, one needs advanced methods beyond the scope of this book. -\begin_inset Foot -status open - -\begin_layout Plain Layout -See the paper by R. + See the paper by R. \begin_inset space ~ \end_inset @@ -26093,11 +25820,68 @@ literal "false" \end_inset +\family default + and the paper by M. +\begin_inset space ~ +\end_inset + +Jaskelioff and R. +\begin_inset space ~ +\end_inset + +O'Connor: +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "https://arxiv.org/abs/1402.1699" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + As an example, consider the functors +\begin_inset Formula $L^{A}\triangleq E\rightarrow A$ +\end_inset + + and +\begin_inset Formula $F^{A}\triangleq Z+A$ +\end_inset + +, where the types +\begin_inset Formula $E$ +\end_inset + + and +\begin_inset Formula $Z$ +\end_inset + + are arbitrary but fixed. + Then the type signature of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +sequence \end_layout \end_inset + +\begin_inset Formula $:L^{F^{B}}\rightarrow F^{L^{B}}$ +\end_inset + + becomes +\begin_inset Formula $(E\rightarrow Z+A)\rightarrow Z+(E\rightarrow A)$ +\end_inset +, which is impossible to implement via a fully parametric function. \end_layout \begin_layout Subsection @@ -26917,7 +26701,7 @@ noprefix "false" ) becomes: \begin_inset Formula \begin{align*} - & \text{seq2}_{S}^{F,G^{A},G^{B}}\bef(\text{seq2}_{S}^{G,A,B})^{\uparrow F}\\ +\text{seq2}_{S}^{F,G^{A},G^{B}}\bef & (\text{seq2}_{S}^{G,A,B})^{\uparrow F}\\ & =\gunderline{\big(\overline{\text{seq2}}_{S}^{F,G^{A},G^{B}}\big)^{\uparrow T^{F^{G^{A}},F^{G^{B}},\bullet}}\bef\big(\overline{\text{seq2}}_{S}^{G,A,B}\big)^{\uparrow F\uparrow T^{F^{G^{A}},F^{G^{B}},\bullet}}}\bef\text{seq3}_{T}^{F,G^{A},G^{B},G^{S^{A,B}}}\bef\big(\text{seq3}_{T}^{G,A,B,S^{A,B}}\big)^{\uparrow F}\\ \text{Eq.~(\ref{eq:identity-law-of-bisequence})}:\quad & =(\text{seq2}_{S}^{F\circ G,A,B})^{\uparrow T^{F^{G^{A}},F^{G^{B}},\bullet}}\bef\gunderline{\text{seq3}_{T}^{F,G^{A},G^{B},G^{S^{A,B}}}\bef\big(\text{seq3}_{T}^{G,A,B,S^{A,B}}\big)^{\uparrow F}}\\ \text{Eq.~(\ref{eq:composition-law-of-trisequence})}:\quad & =(\text{seq2}_{S}^{F\circ G,A,B})^{\uparrow T^{F^{G^{A}},F^{G^{B}},\bullet}}\bef\gunderline{\text{seq3}_{T}^{F\circ G,A,B,S^{A,B}}}\quad. @@ -27019,7 +26803,7 @@ consume \end_inset -, show that +, show that the types of \begin_inset listings inline true status open @@ -27798,14 +27582,14 @@ Prove that for any two monoid morphisms Exercise \begin_inset CommandInset label LatexCommand label -name "subsec:Exercise-traversables-10" +name "subsec:Exercise-traversables-laws-2-1" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Exercise-traversables-10" +reference "subsec:Exercise-traversables-laws-2-1" plural "false" caps "false" noprefix "false" @@ -27816,68 +27600,72 @@ noprefix "false" \end_layout \begin_layout Standard -Given a -\emph on -monad -\emph default - -\begin_inset Formula $M^{\bullet}$ +For any applicative functor +\begin_inset Formula $F$ \end_inset - and a monoid morphism -\begin_inset Formula $\phi:R\rightarrow S$ +, Example +\begin_inset space ~ \end_inset - between some monoid types -\begin_inset Formula $R$ -\end_inset - and -\begin_inset Formula $S$ +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Example-pure-is-applicative-morphism" +plural "false" +caps "false" +noprefix "false" + \end_inset -, prove that -\begin_inset Formula $\phi^{\uparrow M}:M^{R}\rightarrow M^{S}$ + shows that +\begin_inset Formula $\text{pu}_{F}:A\rightarrow F^{A}$ \end_inset - is also a monoid morphism. - (The types -\begin_inset Formula $M^{R}$ + is an applicative morphism between +\begin_inset Formula $\text{Id}$ \end_inset and -\begin_inset Formula $M^{S}$ +\begin_inset Formula $F$ \end_inset - are monoids due to Exercise -\begin_inset space ~ +. + Prove that +\begin_inset Formula $\text{pu}_{F}$ \end_inset + is the +\emph on +only +\emph default + such morphism. + (In terms of category theory, the identity functor is an initial object + in the category of applicative functors +\begin_inset Index idx +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Exercise-monad-of-monoid-is-monoid" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout +category of applicative functors!initial object +\end_layout \end_inset -). +.) \end_layout \begin_layout Subsubsection Exercise \begin_inset CommandInset label LatexCommand label -name "subsec:Exercise-traversables-10-1" +name "subsec:Exercise-traversables-10" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Exercise-traversables-10-1" +reference "subsec:Exercise-traversables-10" plural "false" caps "false" noprefix "false" @@ -27888,10 +27676,82 @@ noprefix "false" \end_layout \begin_layout Standard -Given a monoid type -\begin_inset Formula $R$ -\end_inset - +Given a +\emph on +monad +\emph default + +\begin_inset Formula $M^{\bullet}$ +\end_inset + + and a monoid morphism +\begin_inset Formula $\phi:R\rightarrow S$ +\end_inset + + between some monoid types +\begin_inset Formula $R$ +\end_inset + + and +\begin_inset Formula $S$ +\end_inset + +, prove that +\begin_inset Formula $\phi^{\uparrow M}:M^{R}\rightarrow M^{S}$ +\end_inset + + is also a monoid morphism. + (The types +\begin_inset Formula $M^{R}$ +\end_inset + + and +\begin_inset Formula $M^{S}$ +\end_inset + + are monoids due to Exercise +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-monad-of-monoid-is-monoid" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:Exercise-traversables-10-1" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-traversables-10-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +Given a monoid type +\begin_inset Formula $R$ +\end_inset + and a \emph on monad @@ -27968,7 +27828,11 @@ noprefix "false" \end_layout \begin_layout Standard -Each monad is at the same time an applicative functor. + +\series bold +(a) +\series default + Each monad has at the same time an applicative functor instance. Given a monad morphism \begin_inset Formula $\phi:M\leadsto N$ \end_inset @@ -27988,6 +27852,47 @@ Each monad is at the same time an applicative functor. . \end_layout +\begin_layout Standard + +\series bold +(b) +\series default + Show that the converse does not hold: if +\begin_inset Formula $\phi:M\leadsto N$ +\end_inset + + is an applicative morphism between two monads then +\begin_inset Formula $\phi$ +\end_inset + + is +\emph on +not +\emph default + necessarily a monad morphism. + One example is where +\begin_inset Formula $M$ +\end_inset + + and +\begin_inset Formula $N$ +\end_inset + + are +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +State +\end_layout + +\end_inset + + monads ***??? +\end_layout + \begin_layout Subsubsection Exercise \begin_inset CommandInset label @@ -28119,65 +28024,1715 @@ Given a monoid type \begin_inset space ~ \end_inset - + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-applicative-of-monoid-is-monoid" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:Exercise-traversables-10-3-1" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-traversables-10-3-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +Given a monoid +\begin_inset Formula $M$ +\end_inset + +, define the functions +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +inMF +\end_layout + +\end_inset + + and +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +outMF +\end_layout + +\end_inset + +: +\begin_inset Formula +\begin{align*} + & \text{inMF}:M\rightarrow M\rightarrow M\quad,\quad\quad\text{inMF}(x^{:M})\triangleq y^{:M}\rightarrow x\oplus y\quad,\\ + & \text{outMF}:(M\rightarrow M)\rightarrow M\quad,\quad\quad\text{outMF}(p^{:M\rightarrow M})\triangleq p(e_{M})\quad. +\end{align*} + +\end_inset + +This definition of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +inMF +\end_layout + +\end_inset + + is similar to that used in the proof of Statement +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Statement-foldleft-foldmap-equivalence" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +(c). +\end_layout + +\begin_layout Standard + +\series bold +(a) +\series default + Prove that +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +inMF +\end_layout + +\end_inset + + is a monoid morphism between +\begin_inset Formula $M$ +\end_inset + + and the monoid +\begin_inset Formula $\text{MF}^{M}$ +\end_inset + + consisting of all functions of type +\begin_inset Formula $M\rightarrow M$ +\end_inset + +. + Define the empty element and the binary operation of +\begin_inset Formula $\text{MF}^{M}$ +\end_inset + + appropriately. +\end_layout + +\begin_layout Standard + +\series bold +(b) +\series default + Prove that +\begin_inset Formula $\text{inMF}\bef\text{outMF}=\text{id}$ +\end_inset + + but +\begin_inset Formula $\text{outMF}\bef\text{inMF}\neq\text{id}$ +\end_inset + +. + (Give an example of a monoid +\begin_inset Formula $M$ +\end_inset + + where the second equation does not hold.) +\end_layout + +\begin_layout Standard + +\series bold +(c) +\series default + Prove that +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +outMF +\end_layout + +\end_inset + + is +\emph on +not +\emph default + a monoid morphism between +\begin_inset Formula $\text{MF}^{M}$ +\end_inset + + and +\begin_inset Formula $M$ +\end_inset + +. +\end_layout + +\begin_layout Section +Discussion and further developments +\end_layout + +\begin_layout Subsection +Laws of +\family typewriter +traverse +\family default + and properties of +\family typewriter +zipWithIndex +\family default + +\begin_inset CommandInset label +LatexCommand label +name "subsec:Laws-of-traverse-and-zipWithIndex" + +\end_inset + + +\end_layout + +\begin_layout Standard +Sections +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Decorating-a-tree1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +– +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Decorating-a-tree-breadth-first-traversal" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + defined the method +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + for different orders of tree traversal. + In a similar way, we may define +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + (denoted +\begin_inset Formula $\text{zwi}_{L}$ +\end_inset + + for brevity) for any traversable functor +\begin_inset Formula $L$ +\end_inset + +. + In this section, we will prove some intuitively reasonable properties of + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + by assuming only that the laws of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +traverse +\end_layout + +\end_inset + + hold. + This will serve as an additional evidence that the laws of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +traverse +\end_layout + +\end_inset + + correspond to a programmer's intuitions about code. +\end_layout + +\begin_layout Standard +To implement +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + for +\begin_inset Formula $L$ +\end_inset + +, we use +\begin_inset Formula $L$ +\end_inset + +'s +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +traverse +\end_layout + +\end_inset + + method ( +\begin_inset Formula $\text{trav}_{L}^{F,A,B}$ +\end_inset + +) and chose the applicative functor +\begin_inset Formula $F$ +\end_inset + + as the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +State +\end_layout + +\end_inset + + monad with internal state of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Int +\end_layout + +\end_inset + +: +\begin_inset Formula +\[ +F^{A}\triangleq\text{State}^{\text{Int},A}\triangleq\text{Int}\rightarrow A\times\text{Int}\quad. +\] + +\end_inset + +The internal state represents a current value of the index. + The code of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + applies +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +traverse +\end_layout + +\end_inset + + to a function of type +\begin_inset Formula $A\rightarrow\text{State}^{S,A\times\text{Int}}$ +\end_inset + + that increments the index: +\begin_inset Formula +\begin{equation} +\text{zwi}_{L}^{A}:L^{A}\rightarrow L^{A\times\text{Int}}\quad,\quad\text{zwi}_{L}^{A}\triangleq\text{trav}_{L}^{F,A,A\times\text{Int}}(a^{:A}\rightarrow s^{:\text{Int}}\rightarrow(a\times s)\times(s+1))\bef\text{run}_{\text{State}}(0^{:\text{Int}})\quad,\label{eq:definition-of-zwi} +\end{equation} + +\end_inset + +Here +\begin_inset Formula $\text{run}_{\text{State}}(0^{:\text{Int}})$ +\end_inset + + is the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +State +\end_layout + +\end_inset + + monad's runner defined by Eq. +\begin_inset space ~ +\end_inset + +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:definition-of-runState" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +) and applied to the zero integer value +\begin_inset Formula $0^{:\text{Int}}$ +\end_inset + +: +\begin_inset Formula +\[ +\text{run}_{\text{State}}:S\rightarrow\text{State}^{S,A}\rightarrow A\quad,\quad\quad\text{run}_{\text{State}}(s_{0})\triangleq k^{:S\rightarrow A\times S}\rightarrow s_{0}\triangleright k\triangleright\pi_{1}\quad. +\] + +\end_inset + + +\end_layout + +\begin_layout Standard +What are a programmer's intuitive expectations about +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + when applied to arbitrary traversable functors +\begin_inset Formula $L$ +\end_inset + +? First, +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + applied to a value +\begin_inset Formula $p:L^{A}$ +\end_inset + + should produce a value of type +\begin_inset Formula $L^{A\times\text{Int}}$ +\end_inset + + that preserves the structure of +\begin_inset Formula $p$ +\end_inset + + and just adds indices at places where some data of type +\begin_inset Formula $A$ +\end_inset + + is stored within +\begin_inset Formula $p$ +\end_inset + +. + Second, +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + should produce a +\emph on +different +\emph default + index for every value of type +\begin_inset Formula $A$ +\end_inset + + stored within +\begin_inset Formula $p$ +\end_inset + +. + Let us now formulate those intuitions as equations that rigorously express + the expected properties of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + +. +\end_layout + +\begin_layout Standard +The first property is that the value +\begin_inset Formula $p:L^{A}$ +\end_inset + + must be restored if we remove the index values: +\end_layout + +\begin_layout Standard +\begin_inset Wrap figure +lines 0 +placement l +overhang 0in +width "50col%" +status open + +\begin_layout Plain Layout +\begin_inset VSpace -100baselineskip% +\end_inset + + +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +p.zipWithIndex.map(_._1) == p +\end_layout + +\end_inset + + +\begin_inset VSpace -50baselineskip% +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +\begin_inset space ~ +\end_inset + + +\begin_inset VSpace -50baselineskip% +\end_inset + + +\begin_inset Formula +\[ +p\triangleright\text{zwi}_{L}\triangleright\pi_{1}^{\uparrow L}=p\quad. +\] + +\end_inset + + +\end_layout + +\begin_layout Standard +The second property means that each index can be mapped to a distinct value + of type +\begin_inset Formula $A$ +\end_inset + + stored within +\begin_inset Formula $p$ +\end_inset + +. + Begin by computing a value +\begin_inset Formula $q\triangleq p\triangleright\text{zwi}_{L}\triangleright\pi_{2}^{\uparrow L}$ +\end_inset + + of type +\begin_inset Formula $L^{\text{Int}}$ +\end_inset + +. + Then +\begin_inset Formula $q$ +\end_inset + + has the same structure as +\begin_inset Formula $p$ +\end_inset + + but carries integer index values instead of values of type +\begin_inset Formula $A$ +\end_inset + +. + We expect that the original data ( +\begin_inset Formula $p$ +\end_inset + +) can be restored if we replace the index values in +\begin_inset Formula $q$ +\end_inset + + by the corresponding values of type +\begin_inset Formula $A$ +\end_inset + +. + In other words, there should exist a function +\begin_inset Formula $f:\text{Int}\rightarrow A$ +\end_inset + + such that +\begin_inset Formula $p$ +\end_inset + + can be restored from +\begin_inset Formula $f$ +\end_inset + + and +\begin_inset Formula $q$ +\end_inset + + as +\begin_inset Formula $p=q\triangleright f^{\uparrow L}$ +\end_inset + +. + This also means that there should be an injective map from the type +\begin_inset Formula $L^{A}$ +\end_inset + + to the type +\begin_inset Formula $(\text{Int}\rightarrow A)\times L^{\text{Int}}$ +\end_inset + +. +\end_layout + +\begin_layout Standard +We will now prove the first property of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + +, assuming only that +\begin_inset Formula $L$ +\end_inset + + is a lawful traversable functor. + In that proof, we will need a special subtype of the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +State +\end_layout + +\end_inset + + monad: +\end_layout + +\begin_layout Subsubsection +Statement +\begin_inset CommandInset label +LatexCommand label +name "subsec:Statement-constant-value-state-monad" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Statement-constant-value-state-monad" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +We define a +\begin_inset Quotes eld +\end_inset + +constant-function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +State +\end_layout + +\end_inset + + monad +\begin_inset Quotes erd +\end_inset + +, denoted by +\begin_inset Formula $\text{CFState}^{S,A}$ +\end_inset + +, like this: Write the type of a +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +State +\end_layout + +\end_inset + + monad equivalently as +\begin_inset Formula $\text{State}^{S,A}\triangleq S\rightarrow A\times S\cong(S\rightarrow A)\times(S\rightarrow S)$ +\end_inset + + and consider values of that type, +\begin_inset Formula $p^{:S\rightarrow A}\times q^{:S\rightarrow S}$ +\end_inset + +, such that +\begin_inset Formula $p^{:S\rightarrow A}$ +\end_inset + + is a +\emph on + constant +\emph default + function (independent of its argument). + In other words, the wrapped value of type +\begin_inset Formula $A$ +\end_inset + + is independent of the state value of type +\begin_inset Formula $S$ +\end_inset + +. + This defines a subset of all possible values of type +\begin_inset Formula $\text{State}^{S,A}$ +\end_inset + +. + We call that subset +\begin_inset Formula $\text{CFState}^{S,A}$ +\end_inset + + and write: +\begin_inset Formula +\[ +\text{CFState}^{S,A}\triangleq(\_^{:S}\rightarrow A)\times(S\rightarrow S)\quad. +\] + +\end_inset + +This is a subtype of +\begin_inset Formula $\text{State}^{S,A}$ +\end_inset + + because there is a function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fromCF +\end_layout + +\end_inset + + of type +\begin_inset Formula $\text{CFState}^{S,A}\rightarrow\text{State}^{S,A}$ +\end_inset + + that is an identity function that merely reassigns types: +\begin_inset Formula +\[ +\text{fromCF}:(\_^{:S}\rightarrow A)\times(S\rightarrow S)\rightarrow(S\rightarrow A)\times(S\rightarrow S)\quad,\quad\quad\text{fromCF}\triangleq p^{:\_\rightarrow A}\times q^{:S\rightarrow S}\rightarrow p^{:S\rightarrow A}\times q^{:S\rightarrow S}\quad. +\] + +\end_inset + +Assuming that +\begin_inset Formula $S$ +\end_inset + + is not a void type, we then have the following properties: +\end_layout + +\begin_layout Standard + +\series bold +(a) +\series default + The type +\begin_inset Formula $\text{CFState}^{S,A}$ +\end_inset + + is a monad with the same implementation code as +\begin_inset Formula $\text{State}^{S,A}$ +\end_inset + +. +\end_layout + +\begin_layout Standard + +\series bold +(b) +\series default + The function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fromCF +\end_layout + +\end_inset + + is an injective monad morphism and applicative morphism. +\end_layout + +\begin_layout Standard + +\series bold +(c) +\series default + The monad +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +CFState +\end_layout + +\end_inset + + has a runner, +\begin_inset Formula $\text{run}_{\text{CFState}}:\text{CFState}^{S,A}\rightarrow A$ +\end_inset + +, defined by: +\begin_inset Formula +\[ +\text{run}_{\text{CFState}}\triangleq\text{fromCF}\bef\text{run}_{\text{State}}(s_{0})\quad, +\] + +\end_inset + +where +\begin_inset Formula $s_{0}^{:S}$ +\end_inset + + is a fixed value. + The function +\begin_inset Formula $\text{run}_{\text{CFState}}$ +\end_inset + + is the same for all choices of +\begin_inset Formula $s_{0}$ +\end_inset + + and is both a monad morphism and an applicative morphism. + (Note that +\begin_inset Formula $\text{run}_{\text{State}}(s_{0})$ +\end_inset + + is neither a monad morphism nor an applicative morphism of type +\begin_inset Formula $\text{State}^{S,A}\rightarrow A$ +\end_inset + +.) +\end_layout + +\begin_layout Standard + +\series bold +(d) +\series default + The monad +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +CFState +\end_layout + +\end_inset + + is equivalent to a +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Writer +\end_layout + +\end_inset + + monad. + There exists a one-to-one (bijective) monad morphism +\begin_inset Formula $\text{CFState}^{S,A}\rightarrow\text{Writer}^{W,A}$ +\end_inset + + where +\begin_inset Formula $\text{Writer}^{W,A}\triangleq A\times W$ +\end_inset + + and the type +\begin_inset Formula $W\triangleq S\rightarrow S$ +\end_inset + + is a function composition monoid (denoted by +\begin_inset Formula $\text{MF}^{S}$ +\end_inset + + in the proof of Statement +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Statement-foldleft-foldmap-equivalence" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). +\end_layout + +\begin_layout Subparagraph +Proof +\end_layout + +\begin_layout Standard + +\series bold +(a) +\series default + Rewrite the code of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +State +\end_layout + +\end_inset + + monad's methods +\begin_inset Formula $\text{pu}_{\text{State}}$ +\end_inset + + and +\begin_inset Formula $\text{ftn}_{\text{State}}$ +\end_inset + + using the equivalent type signature +\begin_inset Formula $\text{State}^{S,A}\cong(S\rightarrow A)\times(S\rightarrow S)$ +\end_inset + +: +\begin_inset Formula +\begin{align*} + & \text{pu}_{\text{State}}:A\rightarrow(S\rightarrow A)\times(S\rightarrow S)\quad,\quad\quad\text{pu}_{\text{State}}=a^{:A}\rightarrow(\_^{:S}\rightarrow a)\times\text{id}^{:S\rightarrow S}\quad,\\ + & \text{ftn}_{\text{State}}:(S\rightarrow(S\rightarrow A)\times(S\rightarrow S))\times(S\rightarrow S)\rightarrow(S\rightarrow A)\times(S\rightarrow S)\quad,\\ + & \text{ftn}_{\text{State}}=p^{:S\rightarrow(S\rightarrow A)\times(S\rightarrow S)}\times q^{:S\rightarrow S}\rightarrow(s^{:S}\rightarrow s\triangleright q\triangleright(s\triangleright p\triangleright\pi_{1}))\times(s^{:S}\rightarrow s\triangleright q\triangleright(s\triangleright p\triangleright\pi_{2}))\quad. +\end{align*} + +\end_inset + +The code of +\begin_inset Formula $\text{pu}_{\text{State}}$ +\end_inset + + is already of type +\begin_inset Formula $A\rightarrow\text{CFState}^{S,A}$ +\end_inset + + because +\begin_inset Formula $\text{pu}_{\text{State}}(a)$ +\end_inset + + returns a +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +State +\end_layout + +\end_inset + + monad containing a constant function ( +\begin_inset Formula $\_^{:S}\rightarrow A$ +\end_inset + +). + It remains to check that +\begin_inset Formula $\text{ftn}_{\text{State}}$ +\end_inset + + returns values of the subtype +\begin_inset Formula $\text{CFState}^{S,A}$ +\end_inset + + when applied to a value of type +\begin_inset Formula $\text{CFState}^{S,\text{CFState}^{S,A}}$ +\end_inset + +: +\begin_inset Formula +\begin{align*} + & \text{ftn}_{\text{State}}(p^{:\_^{:S}\rightarrow(\_^{:S}\rightarrow A)\times(S\rightarrow S)}\times q^{:S\rightarrow S})\\ + & =(s^{:S}\rightarrow s\triangleright q\triangleright(\gunderline{s\triangleright p}\triangleright\pi_{1}))\times(s^{:S}\rightarrow...)\\ +\text{denote }p_{0}\triangleq s\triangleright p:\quad & =(s^{:S}\rightarrow\gunderline{s\triangleright q\triangleright\,}(p_{0}\triangleright\pi_{1}))\times(s^{:S}\rightarrow...)\\ +\text{denote }a_{0}\triangleq s\triangleright q\triangleright(p_{0}\triangleright\pi_{1}):\quad & =(s^{:S}\rightarrow a_{0})\times(s^{:S}\rightarrow...)=(\_^{:S}\rightarrow a_{0})\times(s^{:S}\rightarrow...)\quad. +\end{align*} + +\end_inset + +Here the constants +\begin_inset Formula $p_{0}:(\_^{:S}\rightarrow A)\times(S\rightarrow S)$ +\end_inset + + and +\begin_inset Formula $a_{0}:A$ +\end_inset + + exist due to the assumption that both +\begin_inset Formula $p$ +\end_inset + + and +\begin_inset Formula $p_{0}\triangleright\pi_{1}$ +\end_inset + + are constant functions. + It follows that the code of +\begin_inset Formula $\text{pu}_{\text{State}}$ +\end_inset + + and +\begin_inset Formula $\text{ftn}_{\text{State}}$ +\end_inset + + can be reused as +\begin_inset Formula $\text{pu}_{\text{CFState}}$ +\end_inset + + and +\begin_inset Formula $\text{ftn}_{\text{CFState}}$ +\end_inset + + when restricted to the type +\begin_inset Formula $\text{CFState}^{S,A}$ +\end_inset + +. + The monad laws hold for +\begin_inset Formula $\text{CFState}^{S,A}$ +\end_inset + + because those laws hold for the code of +\begin_inset Formula $\text{pu}_{\text{State}}$ +\end_inset + + and +\begin_inset Formula $\text{ftn}_{\text{State}}$ +\end_inset + +. +\end_layout + +\begin_layout Standard + +\series bold +(b) +\series default + The code of the function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fromCF +\end_layout + +\end_inset + + is an identity function that only reassigns types. + This indicates that +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +CFState +\end_layout + +\end_inset + + is a subtype of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +State +\end_layout + +\end_inset + + (in the sense of subtyping explained in Section +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Covariance,-contravariance,-and-subtyping" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). +\end_layout + +\begin_layout Standard +The code of +\begin_inset Formula $\text{pu}_{\text{CFState}}$ +\end_inset + + and +\begin_inset Formula $\text{ftn}_{\text{CFState}}$ +\end_inset + + is the same as the code of +\begin_inset Formula $\text{pu}_{\text{State}}$ +\end_inset + + and +\begin_inset Formula $\text{ftn}_{\text{State}}$ +\end_inset + + respectively. + So, +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fromCF +\end_layout + +\end_inset + + automatically satisfies the laws of the monad morphism of type +\begin_inset Formula $\text{CFState}^{S,A}\rightarrow\text{State}^{S,A}$ +\end_inset + +. + +\end_layout + +\begin_layout Standard +The function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fromCF +\end_layout + +\end_inset + + is then also an applicative morphism (Exercise +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-traversables-10-1-1-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). +\end_layout + +\begin_layout Standard +To prove that +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fromCF +\end_layout + +\end_inset + + is injective, we will show that it has a left inverse (denoted +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +toCF +\end_layout + +\end_inset + +): +\begin_inset Formula +\[ +\text{toCF}:\text{State}^{S,A}\rightarrow\text{CFState}^{S,A}\quad,\quad\quad\text{toCF}\triangleq p^{:S\rightarrow A}\times q^{:S\rightarrow S}\rightarrow(\_^{:S}\rightarrow p(s_{0}))\times q\quad. +\] + +\end_inset + +Here +\begin_inset Formula $s_{0}$ +\end_inset + + is an arbitrary value of type +\begin_inset Formula $S$ +\end_inset + +. + That value exists because +\begin_inset Formula $S$ +\end_inset + + is not a void type. +\end_layout + +\begin_layout Standard +It remains to verify that +\begin_inset Formula $\text{fromCF}\bef\text{toCF}=\text{id}$ +\end_inset + +. + For any constant function +\begin_inset Formula $p\triangleq\_^{:S}\rightarrow a_{0}$ +\end_inset + + we will have +\begin_inset Formula $p(s_{0})=a_{0}$ +\end_inset + + and so we can write: +\begin_inset Formula +\[ +\text{fromCF}\bef\text{toCF}=p^{:\_^{:S}\rightarrow A}\times q^{:S\rightarrow S}\rightarrow(\_^{:S}\rightarrow p(s_{0}))\times q=p\times q\rightarrow(\_\rightarrow a_{0})\times q=p\times q\rightarrow p\times q=\text{id}\quad. +\] + +\end_inset + + +\end_layout + +\begin_layout Standard + +\series bold +(c) +\series default + The code *** +\end_layout + +\begin_layout Standard + +\series bold +(d) +\series default + A value of type +\begin_inset Formula $\text{CFState}^{S,A}=(\_^{:S}\rightarrow A)\times(S\rightarrow S)$ +\end_inset + + is a pair of a constant function and a value of type +\begin_inset Formula $W$ +\end_inset + +. + When +\begin_inset Formula $S$ +\end_inset + + is not void, the type of constant functions +\begin_inset Formula $(\_^{:S}\rightarrow A)$ +\end_inset + + is equivalent to the type +\begin_inset Formula $A$ +\end_inset + +: we can substitute an arbitrary value +\begin_inset Formula $s_{0}:S$ +\end_inset + + into a constant function and obtain the corresponding value of type +\begin_inset Formula $A$ +\end_inset + +. + So, we have the type equivalence +\begin_inset Formula $\text{CFState}^{S,A}\cong A\times(S\rightarrow S)\triangleq A\times W$ +\end_inset + +. + The isomorphism is given by a function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +inCF +\end_layout + +\end_inset + + defined by: +\begin_inset Formula +\[ +\text{inCF}:\text{Writer}^{W,A}\rightarrow\text{CFState}^{S,A}\quad,\quad\quad\text{inCF}\triangleq a^{:A}\times w^{:S\rightarrow S}\rightarrow(\_^{:S}\rightarrow a)\times w\quad. +\] + +\end_inset + + +\end_layout + +\begin_layout Standard +It remains to verify that +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +inCF +\end_layout + +\end_inset + + is a monad morphism. + To verify the identity law: +\begin_inset Formula +\begin{align*} + & \text{pu}_{\text{Writer}}\bef\text{inCF}=(a^{:A}\rightarrow a\times\text{id})\bef(a\times w\rightarrow(\_\rightarrow a)\times w)\\ + & =a\rightarrow(\_\rightarrow a)\times\text{id}=\text{pu}_{\text{State}}=\text{pu}_{\text{CFState}}\quad. +\end{align*} + +\end_inset + + +\begin_inset Formula $\square$ +\end_inset + + +\end_layout + +\begin_layout Standard +We are now ready to prove the first property of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + +. +\end_layout + +\begin_layout Subsubsection +Statement +\begin_inset CommandInset label +LatexCommand label +name "subsec:Statement-properties-of-zipWithIndex" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Statement-properties-of-zipWithIndex" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +For any lawful traversable functor +\begin_inset Formula $L$ +\end_inset + +, define the function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + (denoted for brevity by +\begin_inset Formula $\text{zwi}_{L}$ +\end_inset + +) via Eq. +\begin_inset space ~ +\end_inset + +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:definition-of-zwi" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). + Then +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + satisfies the equation: +\begin_inset Formula $\text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{id}^{:L^{A}\rightarrow L^{A}}$ +\end_inset + +. +\end_layout + +\begin_layout Standard + +\series bold +(b) +\series default + There exists a +\begin_inset Quotes eld +\end_inset + +tabulating +\begin_inset Quotes erd +\end_inset + + function (denoted by +\begin_inset Formula $\text{tab}_{L}^{A}$ +\end_inset + +): +\begin_inset Formula +\[ +\text{tab}_{L}^{A}:L^{A\times\text{Int}}\rightarrow(\text{Int}\rightarrow A)\times L^{\text{Int}}\quad, +\] + +\end_inset + +such that +\begin_inset Formula $\text{zwi}_{L}\bef\text{tab}_{L}\bef(f^{:\text{Int}\rightarrow A}\times q^{:L^{\text{Int}}}\rightarrow q\triangleright f^{\uparrow L})=\text{id}^{:L^{A}\rightarrow L^{A}}$ +\end_inset + +. + So, the function +\begin_inset Formula $\text{zwi}_{L}\bef\text{tab}_{L}$ +\end_inset + + is an injective map of type +\begin_inset Formula $L^{A}\rightarrow(\text{Int}\rightarrow A)\times L^{\text{Int}}$ +\end_inset + +. +\end_layout + +\begin_layout Subparagraph +Proof +\end_layout + +\begin_layout Standard +To make the definition +\begin_inset space ~ +\end_inset + +( \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Exercise-applicative-of-monoid-is-monoid" +reference "eq:definition-of-zwi" plural "false" caps "false" noprefix "false" \end_inset -). -\end_layout - -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:Exercise-traversables-10-3-1" +) easier to work with, we denote: +\begin_inset Formula +\begin{align*} + & F^{A}\triangleq\text{State}^{\text{Int},A}=(\text{Int}\rightarrow A)\times(\text{Int}\rightarrow\text{Int})\quad,\\ + & g:A\rightarrow\text{State}^{\text{Int},A\times\text{Int}}\quad,\quad\quad g\triangleq a^{:A}\rightarrow(s^{:\text{Int}}\rightarrow a\times s)\times(s^{:\text{Int}}\rightarrow s+1)\quad,\\ + & r:\forall B.\,\text{State}^{\text{Int},B}\rightarrow B\quad,\quad\quad r\triangleq p^{:\text{Int}\rightarrow B}\times q^{:\text{Int}\rightarrow\text{Int}}\rightarrow p(0^{:\text{Int}})\quad. +\end{align*} \end_inset - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Exercise-traversables-10-3-1" -plural "false" -caps "false" -noprefix "false" - +and get +\begin_inset Formula $\text{zwi}_{L}=\text{trav}_{L}^{F,A,A\times\text{Int}}(g)\bef r$ \end_inset +. + Then we write: +\begin_inset Formula +\begin{align*} + & \text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{trav}_{L}^{F,A,A\times\text{Int}}(g)\bef\gunderline{r\bef\pi_{1}^{\uparrow L}}\\ +\text{naturality of }r:\quad & =\gunderline{\text{trav}_{L}^{F,A,A\times\text{Int}}(g)\bef\pi_{1}^{\uparrow L\uparrow\text{State}}}\bef r\\ +\text{naturality of }\text{trav}_{L}:\quad & =\text{trav}_{L}^{F,A,A}(g\bef\pi_{1}^{\uparrow\text{State}})\bef r\quad. +\end{align*} -\end_layout +\end_inset -\begin_layout Standard -Given a monoid -\begin_inset Formula $M$ +To compute +\begin_inset Formula $g\bef\pi_{1}^{\uparrow\text{State}}$ \end_inset -, define the functions +, we first express the lifting to \begin_inset listings inline true status open \begin_layout Plain Layout -inMF +State \end_layout \end_inset - and + as: +\begin_inset Formula +\[ +(f^{:A\rightarrow B})^{\uparrow\text{State}^{S,\bullet}}(p^{:S\rightarrow A}\times q^{:S\rightarrow S})=(p\bef f)\times q\quad. +\] + +\end_inset + +Then we get: +\begin_inset Formula +\begin{align*} + & g\bef\pi_{1}^{\uparrow\text{State}}:A\rightarrow\text{State}^{\text{Int},A}\quad,\\ + & g\bef\pi_{1}^{\uparrow\text{State}}=a^{:A}\rightarrow(s^{:\text{Int}}\rightarrow\pi_{1}(a\times s))\times(s^{:\text{Int}}\rightarrow s+1)\\ + & =a\rightarrow(s^{:\text{Int}}\rightarrow a)\times(s^{:\text{Int}}\rightarrow s+1)=a\rightarrow(\_^{:\text{Int}}\rightarrow a)\times(s^{:\text{Int}}\rightarrow s+1)\quad. +\end{align*} + +\end_inset + +Now we note that the sub-expression ( +\begin_inset Formula $\_^{:\text{Int}}\rightarrow a$ +\end_inset + +) is a constant function. + So, +\begin_inset Formula $g\bef\pi_{1}^{\uparrow\text{State}}$ +\end_inset + + can be expressed as a function +\begin_inset Formula $h$ +\end_inset + + of type +\begin_inset Formula $A\rightarrow\text{CFState}^{\text{Int},A}$ +\end_inset + + followed by \begin_inset listings inline true status open \begin_layout Plain Layout -outMF +fromCF \end_layout \end_inset @@ -28185,137 +29740,229 @@ outMF : \begin_inset Formula \begin{align*} - & \text{inMF}:M\rightarrow M\rightarrow M\quad,\quad\quad\text{inMF}(x^{:M})\triangleq y^{:M}\rightarrow x\oplus y\quad,\\ - & \text{outMF}:(M\rightarrow M)\rightarrow M\quad,\quad\quad\text{outMF}(p^{:M\rightarrow M})\triangleq p(e_{M})\quad. + & h:A\rightarrow\text{CFState}^{\text{Int},A}=A\rightarrow(\_^{:\text{Int}}\rightarrow A)\times(\text{Int}\rightarrow\text{Int})\quad,\\ + & h\triangleq a\rightarrow(\_^{:\text{Int}}\rightarrow a)\times(s^{:\text{Int}}\rightarrow s+1)\quad,\quad\quad g\bef\pi_{1}^{\uparrow\text{State}}=h\bef\text{fromCF}\quad. \end{align*} \end_inset -This definition of + +\end_layout + +\begin_layout Standard +In this way, we have found that: +\begin_inset Formula +\[ +\text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{trav}_{L}^{\text{State}^{\text{Int},\bullet},A,A}(h\bef\text{fromCF})\bef r\quad. +\] + +\end_inset + +Since \begin_inset listings inline true status open \begin_layout Plain Layout -inMF +fromCF \end_layout \end_inset - is similar to that used in the proof of Statement + is an applicative morphism, we can use the applicative naturality law \begin_inset space ~ \end_inset - +( \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Statement-foldleft-foldmap-equivalence" +reference "eq:traverse-applicative-naturality-law" plural "false" caps "false" noprefix "false" \end_inset -(c). -\end_layout +) with +\begin_inset Formula $B\triangleq A$ +\end_inset -\begin_layout Standard +, +\begin_inset Formula $F^{A}\triangleq\text{CFState}^{\text{Int},A}$ +\end_inset -\series bold -(a) -\series default - Prove that +, +\begin_inset Formula $G^{A}\triangleq\text{State}^{\text{Int},A}$ +\end_inset + +, +\begin_inset Formula $f^{:F^{B}\rightarrow G^{B}}=$ +\end_inset + + \begin_inset listings inline true status open \begin_layout Plain Layout -inMF +fromCF \end_layout \end_inset - is a monoid morphism between -\begin_inset Formula $M$ +, and +\begin_inset Formula $g^{:A\rightarrow F^{B}}=h$ \end_inset - and the monoid -\begin_inset Formula $\text{MF}^{M}$ +: +\begin_inset Formula +\[ +\text{trav}_{L}^{G,A,B}(g\bef f)=\text{trav}_{L}^{F,A,B}(g)\bef f\quad\quad\text{or equivalently:}\quad\quad\text{trav}_{L}(h\bef\text{fromCF})=\text{trav}_{L}(h)\bef\text{fromCF}\quad. +\] + \end_inset - consisting of all functions of type -\begin_inset Formula $M\rightarrow M$ +Statement +\begin_inset space ~ \end_inset -. - Define the empty element and the binary operation of -\begin_inset Formula $\text{MF}^{M}$ + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Statement-constant-value-state-monad" +plural "false" +caps "false" +noprefix "false" + \end_inset - appropriately. -\end_layout +(c) gives the formula +\begin_inset Formula $\text{run}_{\text{CFState}}=\text{fromCF}\bef r$ +\end_inset -\begin_layout Standard +, so we write: +\begin_inset Formula +\[ +\text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{trav}_{L}(h)\bef\gunderline{\text{fromCF}\bef r}=\text{trav}_{L}(h)\bef\text{run}_{\text{CFState}}\quad. +\] -\series bold -(b) -\series default - Prove that -\begin_inset Formula $\text{inMF}\bef\text{outMF}=\text{id}$ \end_inset - but -\begin_inset Formula $\text{outMF}\bef\text{inMF}\neq\text{id}$ +As +\begin_inset Formula $\text{run}_{\text{CFState}}$ \end_inset -. - (Give an example of a monoid -\begin_inset Formula $M$ + is an applicative morphism, we may again use the applicative naturality + law: +\begin_inset Formula +\[ +\text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{trav}_{L}(h)\bef\text{run}_{\text{CFState}}=\text{trav}_{L}(h\bef\text{run}_{\text{CFState}})\quad. +\] + \end_inset - where the second equation does not hold.) +Now we recall the identity law +\begin_inset space ~ +\end_inset + +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:traverse-identity-law" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +) and compute: +\begin_inset Formula +\begin{align*} + & h\bef\text{run}_{\text{CFState}}=a\rightarrow(\_^{:\text{Int}}\rightarrow a)\times(s^{:\text{Int}}\rightarrow s+1)\bef\text{run}_{\text{CFState}}=a\rightarrow a=\text{id}^{:A\rightarrow A}\quad,\\ + & \text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{trav}_{L}(h\bef\text{run}_{\text{CFState}})=\text{trav}_{L}(\text{id})=\text{id}\quad. +\end{align*} + +\end_inset + + +\begin_inset Formula $\square$ +\end_inset + + \end_layout \begin_layout Standard - -\series bold -(c) -\series default - Prove that +The second property of \begin_inset listings inline true status open \begin_layout Plain Layout -outMF +zipWithIndex \end_layout \end_inset - is + does \emph on not \emph default - a monoid morphism between -\begin_inset Formula $\text{MF}^{M}$ + seem to be provable using the laws of traverse; see Problem +\begin_inset space ~ \end_inset - and -\begin_inset Formula $M$ + +\begin_inset CommandInset ref +LatexCommand ref +reference "par:Problem-traverse-law" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +(b). + However, it appears to be natural to expect that +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithIndex +\end_layout + +\end_inset + + assigns different indices to each value of type +\begin_inset Formula $A$ +\end_inset + + stored inside a data structure of type +\begin_inset Formula $L^{A}$ \end_inset . + Perhaps another law of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +traverse \end_layout -\begin_layout Section -Discussion and further developments +\end_inset + + is needed? \end_layout \begin_layout Subsection -Traversable contrafunctors or profunctors are not useful +Traversable contrafunctors and profunctors are not useful \end_layout \begin_layout Standard @@ -28448,15 +30095,15 @@ The identity law gives \end_inset -The implementation of +No other implementation of \begin_inset Formula $\text{seq}_{L}$ \end_inset - follows unambiguously from the applicative naturality law. + would obey the applicative naturality law and the identity law. \end_layout \begin_layout Standard -We can now verify the composition law +We can verify that the composition law \begin_inset space ~ \end_inset @@ -28470,7 +30117,11 @@ noprefix "false" \end_inset -): +) holds for this +\begin_inset Formula $\text{seq}_{L}$ +\end_inset + +: \begin_inset Formula \begin{align*} \text{left-hand side}:\quad & \text{seq}_{L}^{F,G^{A}}\bef(\text{seq}_{L}^{G,A})^{\uparrow F}=\text{pu}_{F}^{\downarrow L}\bef\gunderline{\text{pu}_{F}\bef(\text{pu}_{G}^{\downarrow L}\bef\text{pu}_{G})^{\uparrow F}}\\ @@ -28480,7 +30131,11 @@ noprefix "false" \end_inset -So, every contrafunctor's + +\end_layout + +\begin_layout Standard +So, every contrafunctor has a unique \begin_inset listings inline true status open @@ -28492,24 +30147,28 @@ sequence \end_inset - method satisfies the laws of traversables. + method that obeys the laws of traversables. + However, the code of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +sequence \end_layout -\begin_layout Standard -However, the code of -\begin_inset Formula $\text{seq}_{L}^{F,A}$ \end_inset - always produces values with empty + ignores all \begin_inset Formula $F$ \end_inset --effects. - Any information about nontrivial +-effects in its arguments and always produces values with empty \begin_inset Formula $F$ \end_inset --effects is ignored. +-effects. For example, if \begin_inset Formula $L^{A}\triangleq A\rightarrow Z$ \end_inset @@ -28548,8 +30207,12 @@ The function \end_inset -effects. - We conclude that contrafunctors are traversable in a way that does not - seem to be practically useful. + +\end_layout + +\begin_layout Standard +We see that contrafunctors are traversable in a way that is not practically + useful. \end_layout \begin_layout Standard @@ -28598,17 +30261,11 @@ Since the applicative functor \end_inset -This implementation ignores its argument +This function is not useful because it ignores its argument \begin_inset Formula $p$ \end_inset -, losing information and always returning an empty -\begin_inset Formula $F$ -\end_inset - --effect. - As with contrafunctors, we find that profunctors are not traversable in - a useful way. +. \end_layout \begin_layout Subsection @@ -28898,10 +30555,6 @@ sequence \end_inset -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{1.4pc}\xyScaleX{6pc}S^{F^{A},L^{P^{F^{A}}}}\ar[r]\sp(0.5){\big(\text{seq}_{P}^{F,A}\big)^{\uparrow L\uparrow S^{F^{A},\bullet}}} & S^{F^{A},L^{F^{P^{A}}}}\ar[r]\sp(0.5){\big(\overline{\text{seq}}_{L}^{F,P^{A}}\big)^{\uparrow S^{F^{A},\bullet}}} & S^{F^{A},F^{L^{P^{A}}}}\ar[r]\sp(0.5){\text{seq2}_{S}^{F,A,L^{P^{A}}}} & F^{S^{A,L^{P^{A}}}}} @@ -28910,10 +30563,6 @@ sequence \end_inset -\begin_inset VSpace -100baselineskip% -\end_inset - - \end_layout \begin_layout Subparagraph diff --git a/sofp-src/sofp-traversable.tex b/sofp-src/sofp-traversable.tex index 87327b73b..6b294b5ce 100644 --- a/sofp-src/sofp-traversable.tex +++ b/sofp-src/sofp-traversable.tex @@ -47,7 +47,7 @@ \subsection{From \texttt{reduce} and \texttt{foldLeft} to \texttt{foldMap} and must satisfy (other than naturality laws that will hold automatically for any fully parametric code). To make progress, we will generalize \lstinline!foldLeft! further to obtain a new function called \lstinline!traverse!, -for which we can motivate non-trivial laws. We will achieve this generalization +for which we can motivate non-trivial laws. This generalization goes in two steps, first transforming \lstinline!foldLeft! to a function called \lstinline!foldMap! and then to \lstinline!traverse!. @@ -150,7 +150,7 @@ \subsection{The \texttt{traverse} operation\label{subsec:The-traverse-operation} If we ignore the implicit argument of type \lstinline!ExecutionContext! (which is specific to the \lstinline!Future! class) and also flip the first two curried arguments, we will arrive at the following type -signature of \lstinline!traverse!:\vspace{-0.5\baselineskip} +signature of \lstinline!traverse!: \[ \text{traverse}:(A\rightarrow\text{Future}^{B})\rightarrow\text{List}^{A}\rightarrow\text{Future}^{\text{List}^{B}}\quad. \] @@ -189,7 +189,7 @@ \subsection{Implementing \texttt{traverse} for various data types} To understand how \lstinline!traverse! works, let us implement it for some data types, including lists and trees. -\subsubsection{Example \label{subsec:Example-traverse-for-1+a*a}\ref{subsec:Example-traverse-for-1+a*a}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-traverse-for-1+a*a}\ref{subsec:Example-traverse-for-1+a*a}\index{examples (with code)}} Implement \lstinline!traverse! for the type constructor $L$ defined by: @@ -559,7 +559,7 @@ \subsection{Aggregating tree-like data by folding. Breadth-first traversal\label will show an example of implementing a breadth-first \lstinline!traverse! for binary trees. -\subsection{Decorating a tree. I. Depth-first traversal} +\subsection{Decorating a tree. I. Depth-first traversal\label{subsec:Decorating-a-tree1}} A \lstinline!traverse! method for a given tree-like data structure may be used to perform various operations on trees, as long as those @@ -636,10 +636,10 @@ \subsection{Decorating a tree. II. Breadth-first traversal\label{subsec:Decorati In Section~\ref{subsec:Aggregating-tree-like-data-bfs}, we have implemented a breadth-first folding for the tree data structure \lstinline!T2[A]!. What additional code does \lstinline!traverse! need? Folding over -\lstinline!T2[A]! merely needs to arrange the leaf values of type -\lstinline!A! in the breadth-first order, but a \lstinline!traverse! -function must return a value of type \lstinline!F[T2[B]]!. This requires -us to sequence the effects of an arbitrary applicative functor \lstinline!F[_]! +\lstinline!T2[A]! merely needs to enumerate the values of type \lstinline!A! +in the breadth-first order, but a \lstinline!traverse! function must +return a value of type \lstinline!F[T2[B]]!. This requires us to +\emph{merge the effects} of an arbitrary applicative functor \lstinline!F[_]! in the breadth-first order, while gathering the values of type \lstinline!B! into a tree structure (\lstinline!T2[B]!) wrapped under \lstinline!F!. The function \lstinline!toListBFS! shown in the previous section @@ -940,8 +940,7 @@ \subsection{Decorating a tree. II. Breadth-first traversal\label{subsec:Decorati traversal order. As a result, we gain flexibility: an arbitrary traversal order may be used with an arbitrary \textsf{``}decoration logic\textsf{''}. -\subsection{The \texttt{Traversable} typeclass. Implementing \texttt{scanLeft} -via \texttt{traverse}} +\subsection{The \texttt{Traversable} typeclass. Implementing \texttt{scanLeft}} We define a \textsf{``}traversable functor\textsf{''} typeclass (called \lstinline!Traversable!) by specifying a \lstinline!traverse! operation, which is convenient @@ -960,7 +959,7 @@ \subsection{The \texttt{Traversable} typeclass. Implementing \texttt{scanLeft} The \lstinline!foldLeft! operation transforms an initial sequence into a result value by updating some internal state. Another function similar to \lstinline!foldLeft! is \lstinline!scanLeft!; the difference -is that in \lstinline!scanLeft!, the result value is the \emph{sequence} +is that \lstinline!scanLeft!\textsf{'}s result value is the \emph{sequence} of all intermediate state values rather than just the last state value. Let us see how the functionality of \lstinline!scanLeft! can be expressed using \lstinline!traverse! with a \lstinline!State! monad that handles @@ -1410,7 +1409,7 @@ \subsection{Recursion schemes. II. Unfolding operations} To get more intuition, we look at some examples using \lstinline!unfold! with lists and binary trees. -\subsubsection{Example \label{subsec:Example-unfold-list}\ref{subsec:Example-unfold-list}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-unfold-list}\ref{subsec:Example-unfold-list}\index{examples (with code)}} Use \lstinline!unfold! to create a \lstinline!List! of consecutive powers of $2$ up to a given value $n$: @@ -1531,20 +1530,12 @@ \subsubsection{Example \label{subsec:Example-unfold-tree}\ref{subsec:Example-unf \end{lstlisting} This completes the implementation of \lstinline!fullBinaryTree!. -To test the resulting code, compute a full tree - -\begin{wrapfigure}{l}{0.74\columnwidth}% -\vspace{-0.85\baselineskip} +To test the resulting code, compute a full tree of depth $2$:~{\tiny{}\Tree[ [ 0 1 ] [ 2 3 ] ]} \begin{lstlisting} scala> fullBinaryTree(2) res0: T2[Int] = Branch(Branch(Leaf(0), Leaf(1)), Branch(Leaf(2), Leaf(3))) \end{lstlisting} -\vspace{0.5\baselineskip} -\end{wrapfigure}% - -\noindent of depth $2$:~{\tiny{}\Tree[ [ 0 1 ] [ 2 3 ] ]}\\ -~ \subsubsection{Example \label{subsec:Example-unfold-tree-evenodd}\ref{subsec:Example-unfold-tree-evenodd}} @@ -1687,9 +1678,9 @@ \subsubsection{Example \label{subsec:Example-unfold-tree-evenodd}\ref{subsec:Exa if (makeLeaf) Left(z) else Right(_ => ((z + 1, true), (z + 1, false))) }((0, false)) \end{lstlisting} -The value \lstinline!tree1toInf! is finite but can compute on demand -a potentially unbounded number of leaves. To visualize \lstinline!tree1toInf!, -we write a function that converts \lstinline!UT[A]! to \lstinline!T2[A]! +The value \lstinline!tree1toInf! is finite but can compute a tree +of unbounded depth. To visualize \lstinline!tree1toInf!, we write +a function that converts \lstinline!UT[A]! to \lstinline!T2[A]! by stopping at a given maximum depth. The unevaluated parts of the tree will be marked with a value called \lstinline!default!: \begin{lstlisting} @@ -1765,8 +1756,8 @@ \subsection{Recursion schemes. III. Traversing operations} and unfolding. To illustrate this, let us implement \lstinline!zipWithDepth! via -a traversal with a binary tree recursion scheme. We will use the \lstinline!State! -monad as the functor $F$: +$\text{trav}_{S}$ with a binary tree recursion scheme. We will use +the \lstinline!State! monad as the functor $F$: \begin{lstlisting} final case class St[A](run: Int => (A, Int)) { // A State monad with internal state of type Int. import io.chymyst.ch.implement // Implement the monad methods automatically. @@ -1991,17 +1982,11 @@ \subsubsection{Definition \label{subsec:Definition-monoid-morphism}\ref{subsec:D The \textbf{monoidal naturality law} \index{monoidal naturality law}of \lstinline!reduceE! is then formulated as: -\begin{wrapfigure}{l}{0.2\columnwidth}% -\vspace{-2.15\baselineskip} \[ \xymatrix{\xyScaleY{1.4pc}\xyScaleX{3.5pc}L^{M}\ar[r]\sp(0.5){\ \text{reduceE}^{M}}\ar[d]\sp(0.45){\,f^{\uparrow L}} & M\ar[d]\sp(0.45){\,f}\\ L^{N}\ar[r]\sp(0.5){~\text{reduceE}^{N}} & N } \] -\vspace{-0.6\baselineskip} -\end{wrapfigure}% - -~\vspace{-0.4\baselineskip} \begin{equation} \text{reduceE}^{M}\bef f^{:M\rightarrow N}=f^{\uparrow L}\bef\text{reduceE}^{N}\quad.\label{eq:monoidal-naturality-law-of-reduceE} \end{equation} @@ -2328,13 +2313,13 @@ \subsubsection{Statement \label{subsec:relational-property-for-foldFn}\ref{subse {\color{greenunder}\text{equivalently}:}\quad & (k^{:A\rightarrow A}\rightarrow k(a_{0}))\bef\phi(p)\overset{!}{=}\phi(p\triangleright\text{inF}^{\uparrow L})\bef(k^{:A\rightarrow A}\rightarrow k(a_{0}))\quad,\\ {\color{greenunder}\text{equivalently}:}\quad & k^{:A\rightarrow A}\rightarrow\phi(p)(k(a_{0}))\overset{!}{=}k^{:A\rightarrow A}\rightarrow\phi(p\triangleright\text{inF}^{\uparrow L})(k)(a_{0})\quad. \end{align*} -The last equation can be applied to any function $k$ of type $A\rightarrow A$:\vspace{-0.3\baselineskip} +The last equation can be applied to any function $k$ of type $A\rightarrow A$: \[ \phi(p)(k(a_{0}))\overset{!}{=}\phi(p\triangleright\text{inF}^{\uparrow L})(k)(a_{0})\quad. \] In Eq.~(\ref{eq:first-special-law-of-phi}), the function $\phi(p\triangleright\text{inF}^{\uparrow L})$ is applied to the identity function of type $A\rightarrow A$. So, -we set $k\triangleq\text{id}$ in the line above:\vspace{-0.3\baselineskip} +we set $k\triangleq\text{id}$ in the line above: \[ \phi(p)(a_{0})\overset{!}{=}\phi(p\triangleright\text{inF}^{\uparrow L})(\text{id})(a_{0})\quad. \] @@ -2364,7 +2349,7 @@ \subsubsection{Statement \label{subsec:relational-property-for-foldFn}\ref{subse monoid morphism $f$. So, the precondition of Eq.~(\ref{eq:strong-dinaturality-law-of-phi-with-f}) holds, and we may use its conclusion: \[ -f\bef\text{foldFn}\,(x)\overset{!}{=}\text{foldFn}(y)\bef f\quad\text{or equivalently}:\quad\text{foldFn}\,(p\triangleright\text{inMF}^{\uparrow L})\bef f=f\bef\text{foldFn}(p\triangleright f^{\uparrow L}\bef\text{inMF}^{\uparrow L}). +f\bef\text{foldFn}\,(x)\overset{!}{=}\text{foldFn}(y)\bef f\quad\text{or equivalently:}\quad\text{foldFn}\,(p\triangleright\text{inMF}^{\uparrow L})\bef f=f\bef\text{foldFn}(p\triangleright f^{\uparrow L}\bef\text{inMF}^{\uparrow L}). \] This gives the law~(\ref{eq:foldFn-second-special-law}) if we use $f$\textsf{'}s monoid morphism identity law: $e_{M}\triangleright f=e_{N}$. @@ -2639,14 +2624,14 @@ \subsection{All polynomial functors are foldable} finding correct implementations of folding operations. The reason is that folding operations are available only for polynomial functors, such as \lstinline!Option[A]! and \lstinline!List[A]! (note that -\lstinline!List! is a recursively defined polynomial functor\index{polynomial functor!recursive}). -These functors represent containers that store a finite number of -values of type \lstinline!A!. So, it is clear what it means to extract -\textsf{``}all values of type $A$\textsf{''} from such containers. To show that all -polynomial functors are foldable, we will define \lstinline!toList! -inductively via structural analysis. The definition will ensure that -\lstinline!toList! extracts exactly as many values as stored in the -polynomial functor. +\lstinline!List! is a \emph{recursively} \emph{defined} polynomial +functor\index{polynomial functor!recursive}). Those functors represent +data structures that store a finite number of values of type \lstinline!A!. +It is clear what it means to extract \textsf{``}all values of type $A$\textsf{''} +from such data structures. To show that all polynomial functors are +foldable, we will define \lstinline!toList! inductively via structural +analysis of functor types. The definition will ensure that \lstinline!toList! +correctly extracts the values stored by a given data structure. Let us first show that non-polynomial functors are \emph{not} foldable. An example of a non-polynomial functor is the \lstinline!Reader! @@ -2662,7 +2647,7 @@ \subsection{All polynomial functors are foldable} all values of a given type (as an example, consider the type $R\triangleq\text{String}\rightarrow\text{String}$). So, we will not be able to implement \lstinline!toList!, \lstinline!foldMap!, \lstinline!foldLeft!, or \lstinline!reduce! for the \lstinline!Reader! -monad. The only exceptions are types $R$ that have a known finite +type. The only exceptions are types $R$ that have a known finite set of distinct values, such as $R=$ \lstinline!Boolean!. However, in those cases the type $R\rightarrow A$ is equivalent to a polynomial functor: @@ -2867,7 +2852,6 @@ \subsection{Laws of \texttt{traverse}} \begin{equation} \text{trav}_{L}^{F,X,B}(f\bef g)=f^{\uparrow L}\bef\text{trav}_{L}^{F,A,B}(g)\quad,\quad\quad\text{trav}_{L}^{F,A,C}(g\bef h^{\uparrow F})=\text{trav}_{L}^{F,A,B}(g)\bef h^{\uparrow L\uparrow F}\quad.\label{eq:naturality-laws-of-traverse} \end{equation} -\vspace{-1.2\baselineskip} \[ \xymatrix{\xyScaleY{2.0pc}\xyScaleX{5.0pc}L^{X}\ar[rd]\sp(0.6){~~\text{trav}_{L}^{F,X,B}(f\bef g)}\ar[d]\sp(0.45){f^{\uparrow L}} & & L^{A}\ar[rd]\sb(0.35){\text{trav}_{L}^{F,A,C}(g\bef h)~~}\ar[r]\sp(0.5){\text{trav}_{L}^{F,A,B}(g)} & F^{L^{B}}\ar[d]\sp(0.45){h^{\uparrow L\uparrow F}}\\ L^{A}\ar[r]\sb(0.5){\text{trav}_{L}^{F,A,B}(g)} & F^{L^{B}} & & F^{L^{C}} @@ -2880,35 +2864,25 @@ \subsection{Laws of \texttt{traverse}} not inspect the type of \lstinline!F! directly and make decisions based on that type. The code of \lstinline!traverse! may only use $F$\textsf{'}s applicative methods (\lstinline!wu! and \lstinline!zip!). -To formulate this \textbf{applicative naturality law}\index{applicative naturality law!of traverse@of \texttt{traverse}}, +To formulate this requirement as an \textbf{applicative naturality +law}\index{applicative naturality law!of traverse@of \texttt{traverse}}, we write an equation similar to the second naturality law, except that the arbitrary function $f$ will now map the applicative functor -$F$ to another arbitrary applicative functor $G$: - -\begin{wrapfigure}{l}{0.26\columnwidth}% -\vspace{-1.3\baselineskip} -$\xymatrix{\xyScaleY{1.7pc}\xyScaleX{5.0pc}L^{A}\ar[rd]\sb(0.4){\text{trav}_{L}^{G,A,B}(g\bef f)~~}\ar[r]\sp(0.5){\text{trav}_{L}^{F,A,B}(g)} & F^{L^{B}}\ar[d]\sp(0.45){f}\\ +$F$ to another \emph{arbitrary} applicative functor $G$:$\xymatrix{\xyScaleY{1.7pc}\xyScaleX{5.0pc}L^{A}\ar[rd]\sb(0.4){\text{trav}_{L}^{G,A,B}(g\bef f)~~}\ar[r]\sp(0.5){\text{trav}_{L}^{F,A,B}(g)} & F^{L^{B}}\ar[d]\sp(0.45){f}\\ & G^{L^{B}} } -$\vspace{0.5\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.5\baselineskip} - +$ \begin{equation} \text{trav}_{L}^{G,A,B}(g^{:A\rightarrow F^{B}}\bef f^{:F^{B}\rightarrow G^{B}})=\text{trav}_{L}^{F,A,B}(g)\bef f^{:F^{L^{B}}\rightarrow G^{L^{B}}}\quad.\label{eq:traverse-applicative-naturality-law} \end{equation} - -\noindent Here $f$ is a natural transformation with type signature -$f:\forall X.\,F^{X}\rightarrow G^{X}$. So, we are allowed to apply -the same code of $f$ to different types: $f$ has type $F^{B}\rightarrow G^{B}$ -in the left-hand side of the law and $F^{L^{B}}\rightarrow G^{L^{B}}$ -in the right-hand side. - -In addition to being a natural transformation, the function $f$ must -preserve the applicative typeclass. In other words, $f$ should map -$F$\textsf{'}s applicative methods (\lstinline!wu! and \lstinline!zip!) -to the corresponding methods of $G$. Otherwise, the code of $\text{trav}_{L}^{F,A,B}$ +Here $f$ is a natural transformation with type signature $f:\forall X.\,F^{X}\rightarrow G^{X}$. +So, we are allowed to apply the same code of $f$ to different types: +$f$ has type $F^{B}\rightarrow G^{B}$ in the left-hand side of the +law and $F^{L^{B}}\rightarrow G^{L^{B}}$ in the right-hand side. + +Another requirement for the function $f$ is that $f$ must map $F$\textsf{'}s +applicative methods (\lstinline!wu! and \lstinline!zip!) to the +corresponding methods of $G$. Otherwise, the code of $\text{trav}_{L}^{F,A,B}$ that uses those methods of $F$ will not be mapped via $f$ to the same code of $\text{trav}_{L}^{G,A,B}$ that uses $G$\textsf{'}s applicative methods. @@ -2927,7 +2901,7 @@ \subsection{Laws of \texttt{traverse}} Here are some examples of applicative morphisms and applicative naturality. -\subsubsection{Example \label{subsec:Example-some-applicative-morphisms}\ref{subsec:Example-some-applicative-morphisms}\index{solved examples}} +\subsubsection{Example \label{subsec:Example-some-applicative-morphisms}\ref{subsec:Example-some-applicative-morphisms}\index{examples (with code)}} Consider applicative functors $\text{Id}^{A}\triangleq A$ and $L^{A}\triangleq A\times A$. @@ -3132,7 +3106,6 @@ \subsubsection{Example \label{subsec:Example-pure-is-applicative-morphism}\ref{s & \text{zip}_{F}\big(\text{pu}_{F}(p^{:A})\times\text{pu}_{F}(q^{:B})\big)\overset{?}{=}\text{pu}_{F}(\text{zip}_{\text{Id}}(p\times q))=\text{pu}_{F}(p\times q)\\ {\color{greenunder}\text{use Exercise~\ref{subsec:Exercise-zip-pure-pure}}:}\quad & =\text{zip}_{F}(\text{pu}_{F}(p)\times\text{pu}_{F}(q))\quad. \end{align*} - $\square$ We have analyzed the expectation that \lstinline!traverse! should @@ -3165,7 +3138,7 @@ \subsubsection{Example \label{subsec:Example-pure-is-applicative-morphism}\ref{s \textbf{identity law}\index{identity laws!of traverse@of \texttt{traverse}} of \lstinline!traverse!: \begin{equation} -\text{trav}_{L}^{\text{Id},A,A}(\text{id})=\text{id}\quad.\label{eq:traverse-identity-law} +\text{trav}_{L}^{\text{Id},A,A}(\text{id}^{:A\rightarrow A})=\text{id}^{:L^{A}\rightarrow L^{A}}\quad.\label{eq:traverse-identity-law} \end{equation} We can show that this law is equivalent to Eq.~(\ref{eq:traverse-identity-law-with-pure}): @@ -3219,39 +3192,33 @@ \subsubsection{Statement \label{subsec:Statement-identity-law-traverse-simplifie found by composing two \lstinline!traverse! operations. Consider two different applicative functors ($F$ and $G$), two functions $f:A\rightarrow F^{B}$ and $g:B\rightarrow G^{C}$, and compute the -following composition:\vspace{-0.15\baselineskip} +following composition: \[ p\triangleright\text{trav}_{L}^{F,A,B}(f)\triangleright\big(\text{trav}_{L}^{G,B,C}(g)\big)^{\uparrow F}:F^{G^{L^{C}}}\quad. \] -\vspace{-0.65\baselineskip} \noindent The second \lstinline!traverse! operation needs to be lifted to \lstinline!F! for the types to match. The result (of type $F^{G^{L^{C}}}$) looks like a \lstinline!traverse! operation with respect to the functor $F\circ G$. By Statement~\ref{subsec:Statement-applicative-composition}, the functor $F\circ G$ is applicative. So, we may apply a single -\lstinline!traverse! operation using that functor and obtain:\vspace{-0.5\baselineskip} +\lstinline!traverse! operation using that functor and obtain: \[ p\triangleright\text{trav}_{L}^{F\circ G,A,C}(f\bef g^{\uparrow F}):F^{G^{L^{C}}}\quad. \] - -\begin{wrapfigure}{l}{0.38\columnwidth}% -\vspace{-1.6\baselineskip} $\xymatrix{\xyScaleY{1.5pc}\xyScaleX{3.4pc}L^{A}\ar[dr]\sb(0.35){\text{trav}_{L}^{F\circ G,A,C}(f\bef g^{\uparrow F})~~}\ar[r]\sp(0.5){\text{trav}_{L}^{F,A,B}(f)} & F^{L^{B}}\ar[d]\sp(0.4){\big(\text{trav}_{L}^{G,B,C}(g)\big)^{\uparrow F}}\\ & F^{G^{L^{C}}} } -$\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent The \textbf{composition law}\index{composition law!of traverse@of \texttt{traverse}} +$The \textbf{composition law}\index{composition law!of traverse@of \texttt{traverse}} of \lstinline!traverse! says that the two results should be equal: \begin{equation} -\negthickspace\negthickspace\negthickspace\negthickspace\text{trav}_{L}^{F,A,B}(f)\bef\big(\text{trav}_{L}^{G,B,C}(g)\big)^{\uparrow F}=\text{trav}_{L}^{F\circ G,A,C}(f\bef g^{\uparrow F})\quad.\label{eq:composition-law-of-traverse} +\text{trav}_{L}^{F,A,B}(f)\bef\big(\text{trav}_{L}^{G,B,C}(g)\big)^{\uparrow F}=\text{trav}_{L}^{F\circ G,A,C}(f\bef g^{\uparrow F})\quad.\label{eq:composition-law-of-traverse} \end{equation} -So far, we have not given a motivation for the law~(\ref{eq:composition-law-of-traverse}). -We just wrote that law by analogy with composition laws of other \textsf{``}lifting\textsf{''}-like -functions. It is not obvious that the law~(\ref{eq:composition-law-of-traverse}) +So far, we have not given a motivation for the law~(\ref{eq:composition-law-of-traverse}) +from a programmer\textsf{'}s perspective. We just wrote that law by analogy +with composition laws of other \textsf{``}lifting\textsf{''}-like functions. It is +not obvious that the law~(\ref{eq:composition-law-of-traverse}) indeed prevents \lstinline!traverse! from evaluating some effects more than once. To get a heuristic explanation, assume that \lstinline!traverse! evaluates some effect twice. The left-hand side of the law~(\ref{eq:composition-law-of-traverse}) @@ -3348,10 +3315,10 @@ \subsubsection{Statement \label{subsec:Statement-identity-law-traverse-simplifie \end{figure} We have shown that \emph{some} incorrect implementations of \lstinline!traverse! -are excluded by the composition laws. But it remains unknown (Problem~\ref{par:Problem-traverse-law}) -whether the composition law~(\ref{eq:composition-law-of-traverse}) -always guarantees that the traverse function collects each $F$-effect -exactly once. +are excluded by the composition laws. But it remains unknown whether +the known three laws always guarantee that the traverse function collects +each $F$-effect exactly once. Perhaps another new law of \lstinline!traverse! +is required (see Problem~\ref{par:Problem-traverse-law}). \subsection{Laws of \texttt{sequence}} @@ -3360,26 +3327,16 @@ \subsection{Laws of \texttt{sequence}} Begin with the naturality laws. Since the type signature of \lstinline!sequence! ($L^{F^{A}}\rightarrow F^{L^{A}}$) has only one type parameter, \lstinline!sequence! -has only one naturality law\index{naturality law!of sequence@of \texttt{sequence}}:% -\begin{comment} -precarious formatting -\end{comment} +has only one naturality law\index{naturality law!of sequence@of \texttt{sequence}}: -\begin{wrapfigure}{i}{0.22\columnwidth}% -\vspace{-1.3\baselineskip} $\xymatrix{\xyScaleY{1.5pc}\xyScaleX{2.0pc}L^{F^{A}}\ar[d]\sb(0.45){\text{seq}_{L}^{F,A}}\ar[r]\sb(0.5){f^{\uparrow F\uparrow L}} & L^{F^{B}}\ar[d]\sp(0.45){\text{seq}_{L}^{F,B}}\\ F^{L^{A}}\ar[r]\sp(0.5){f^{\uparrow L\uparrow F}} & F^{L^{B}} } -$\vspace{1.5\baselineskip} -\end{wrapfigure}% - -~\vspace{-1.4\baselineskip} - +$ \begin{equation} (f^{:A\rightarrow B})^{\uparrow F\uparrow L}\bef\text{seq}_{L}^{F,B}=\text{seq}_{L}^{F,A}\bef f^{\uparrow L\uparrow F}\quad.\label{eq:sequence-naturality-law} \end{equation} - -\noindent It will be shown in Exercise~\ref{subsec:Exercise-traversables-laws-2} +It will be shown in Exercise~\ref{subsec:Exercise-traversables-laws-2} that this naturality law is equivalent to the two naturality laws of \lstinline!traverse!. @@ -3395,16 +3352,10 @@ \subsection{Laws of \texttt{sequence}} Exercise~\ref{subsec:Exercise-simplify-law-omit-lifted-function} for a similar property). -\begin{wrapfigure}{l}{0.22\columnwidth}% -\vspace{-1.1\baselineskip} $\xymatrix{\xyScaleY{1.5pc}\xyScaleX{2.0pc}L^{F^{A}}\ar[d]\sb(0.45){\text{seq}_{L}^{F,A}}\ar[r]\sb(0.5){(f^{A})^{\uparrow L}} & L^{G^{A}}\ar[d]\sp(0.45){\text{seq}_{L}^{G,A}}\\ F^{L^{A}}\ar[r]\sp(0.5){f^{L^{A}}} & G^{L^{A}} } -$\vspace{-0.5\baselineskip} -\end{wrapfigure}% - -\noindent Renaming $B$ to $A$, we rewrite the applicative naturality -law\index{applicative naturality law!of sequence@of \texttt{sequence}} +$Renaming $B$ to $A$, we rewrite the applicative naturality law\index{applicative naturality law!of sequence@of \texttt{sequence}} of \lstinline!sequence! as: \[ (f^{A})^{\uparrow L}\bef\text{seq}_{L}^{G,A}=\text{seq}_{L}^{F,A}\bef f^{L^{A}}\quad. @@ -3433,16 +3384,10 @@ \subsection{Laws of \texttt{sequence}} {\color{greenunder}\text{naturality law~(\ref{eq:sequence-naturality-law})}:}\quad & \quad=f^{\uparrow L}\bef g^{\uparrow F\uparrow L}\bef\text{seq}_{L}^{F,G^{C}}\bef(\text{seq}_{L}^{G,C})^{\uparrow F}\quad,\\ {\color{greenunder}\text{right-hand side}:}\quad & \text{trav}_{L}^{F\circ G,A,C}(f\bef g^{\uparrow F})=(f\bef g^{\uparrow F})^{\uparrow L}\bef\text{seq}_{L}^{F\circ G,C}\quad. \end{align*} - -\begin{wrapfigure}{l}{0.3\columnwidth}% -\vspace{-1.7\baselineskip} $\xymatrix{\xyScaleY{1.4pc}\xyScaleX{3.0pc}L^{F^{G^{A}}}\ar[r]\sp(0.55){\text{seq}_{L}^{F,G^{A}}}\ar[rd]\sb(0.4){\text{seq}_{L}^{F\circ G,A}} & F^{L^{G^{A}}}\ar[d]\sp(0.4){(\text{seq}_{L}^{G,A})^{\uparrow F}}\\ & F^{G^{L^{A}}} } -$\vspace{-1.9\baselineskip} -\end{wrapfigure}% - -\noindent Omitting the common function $(f\bef g^{\uparrow F})^{\uparrow L}$ +$Omitting the common function $(f\bef g^{\uparrow F})^{\uparrow L}$ and renaming $C$ to $A$, we obtain the \index{composition law!of sequence@of \texttt{sequence}}\textbf{composition law} of \lstinline!sequence!: \begin{equation} @@ -3555,6 +3500,8 @@ \subsection{All polynomial functors are traversable\label{subsec:All-polynomial- \] \begin{comment} +{*}{*}{*} do we need this? + It helps to rewrite $\text{seq}_{L}$ as a composition of two matrices, separating the functions lifted to $F$: \begin{align*} @@ -3731,14 +3678,14 @@ \subsection{All polynomial functors are traversable\label{subsec:All-polynomial- If the traversal order needs to be chosen differently, we will need to provide a custom implementation of a \lstinline!Traversable! instance. -Non-polynomial functors are, in general, not traversable. As an example, -consider the functors $L^{A}\triangleq E\rightarrow A$ and $F^{A}\triangleq Z+A$, -where the types $E$ and $Z$ are arbitrary but fixed. It is impossible -to implement a fully parametric function with the type signature of -\lstinline!sequence! ($L^{F^{B}}\rightarrow F^{L^{B}}$), which is -$(E\rightarrow Z+A)\rightarrow Z+(E\rightarrow A)$. To prove that -any traversable functor \emph{must} be polynomial, one needs advanced -methods beyond the scope of this book.\footnote{See the paper by R.~Bird\index{Richard Bird} et al.: \texttt{\href{http://www.cs.ox.ac.uk/jeremy.gibbons/publications/uitbaf.pdf}{http://www.cs.ox.ac.uk/jeremy.gibbons/publications/uitbaf.pdf}}} +Functors that are not equivalent to polynomial functors are not traversable.\footnote{To prove that any traversable functor \emph{must} be polynomial, one +needs advanced methods beyond the scope of this book. See the paper +by R.~Bird\index{Richard Bird} et al.: \texttt{\href{http://www.cs.ox.ac.uk/jeremy.gibbons/publications/uitbaf.pdf}{http://www.cs.ox.ac.uk/jeremy.gibbons/publications/uitbaf.pdf}} +and the paper by M.~Jaskelioff and R.~O\textsf{'}Connor: \texttt{\href{https://arxiv.org/abs/1402.1699}{https://arxiv.org/abs/1402.1699}}} As an example, consider the functors $L^{A}\triangleq E\rightarrow A$ +and $F^{A}\triangleq Z+A$, where the types $E$ and $Z$ are arbitrary +but fixed. Then the type signature of \lstinline!sequence! $:L^{F^{B}}\rightarrow F^{L^{B}}$ +becomes $(E\rightarrow Z+A)\rightarrow Z+(E\rightarrow A)$, which +is impossible to implement via a fully parametric function. \subsection{All polynomial bifunctors are bitraversable\label{subsec:All-polynomial-bifunctors-are-bitraversable}} @@ -3959,7 +3906,7 @@ \subsection{All polynomial bifunctors are bitraversable\label{subsec:All-polynom Then the left-hand side of the law~(\ref{eq:composition-law-of-bisequence}) becomes: \begin{align*} - & \text{seq2}_{S}^{F,G^{A},G^{B}}\bef(\text{seq2}_{S}^{G,A,B})^{\uparrow F}\\ +\text{seq2}_{S}^{F,G^{A},G^{B}}\bef & (\text{seq2}_{S}^{G,A,B})^{\uparrow F}\\ & =\gunderline{\big(\overline{\text{seq2}}_{S}^{F,G^{A},G^{B}}\big)^{\uparrow T^{F^{G^{A}},F^{G^{B}},\bullet}}\bef\big(\overline{\text{seq2}}_{S}^{G,A,B}\big)^{\uparrow F\uparrow T^{F^{G^{A}},F^{G^{B}},\bullet}}}\bef\text{seq3}_{T}^{F,G^{A},G^{B},G^{S^{A,B}}}\bef\big(\text{seq3}_{T}^{G,A,B,S^{A,B}}\big)^{\uparrow F}\\ {\color{greenunder}\text{Eq.~(\ref{eq:identity-law-of-bisequence})}:}\quad & =(\text{seq2}_{S}^{F\circ G,A,B})^{\uparrow T^{F^{G^{A}},F^{G^{B}},\bullet}}\bef\gunderline{\text{seq3}_{T}^{F,G^{A},G^{B},G^{S^{A,B}}}\bef\big(\text{seq3}_{T}^{G,A,B,S^{A,B}}\big)^{\uparrow F}}\\ {\color{greenunder}\text{Eq.~(\ref{eq:composition-law-of-trisequence})}:}\quad & =(\text{seq2}_{S}^{F\circ G,A,B})^{\uparrow T^{F^{G^{A}},F^{G^{B}},\bullet}}\bef\gunderline{\text{seq3}_{T}^{F\circ G,A,B,S^{A,B}}}\quad. @@ -3976,8 +3923,8 @@ \subsubsection{Exercise \label{subsec:Exercise-traversables-1}\ref{subsec:Exerci \text{consume}_{L}:(L^{A}\rightarrow B)\rightarrow L^{F^{A}}\rightarrow F^{B}\quad, \] defined for any applicative functor $F$. Assuming a suitable naturality -law for \lstinline!consume!, show that \lstinline!sequence! and -\lstinline!consume! are equivalent. +law for \lstinline!consume!, show that the types of \lstinline!sequence! +and \lstinline!consume! are equivalent. \subsubsection{Exercise \label{subsec:Exercise-traversables-3-1-1}\ref{subsec:Exercise-traversables-3-1-1}} @@ -4070,6 +4017,14 @@ \subsubsection{Exercise \label{subsec:Exercise-traversables-10-2}\ref{subsec:Exe composition $\phi\bef\psi:M\rightarrow P$ is again a lawful monoid morphism. +\subsubsection{Exercise \label{subsec:Exercise-traversables-laws-2-1}\ref{subsec:Exercise-traversables-laws-2-1}} + +For any applicative functor $F$, Example~\ref{subsec:Example-pure-is-applicative-morphism} +shows that $\text{pu}_{F}:A\rightarrow F^{A}$ is an applicative morphism +between $\text{Id}$ and $F$. Prove that $\text{pu}_{F}$ is the +\emph{only} such morphism. (In terms of category theory, the identity +functor is an initial object in the category of applicative functors\index{category of applicative functors!initial object}.) + \subsubsection{Exercise \label{subsec:Exercise-traversables-10}\ref{subsec:Exercise-traversables-10}} Given a \emph{monad} $M^{\bullet}$ and a monoid morphism $\phi:R\rightarrow S$ @@ -4086,10 +4041,15 @@ \subsubsection{Exercise \label{subsec:Exercise-traversables-10-1}\ref{subsec:Exe \subsubsection{Exercise \label{subsec:Exercise-traversables-10-1-1-1}\ref{subsec:Exercise-traversables-10-1-1-1}} -Each monad is at the same time an applicative functor. Given a monad -morphism $\phi:M\leadsto N$ between two monads, show that $\phi$ -is also an applicative morphism between applicative functors $M$ -and $N$. +\textbf{(a)} Each monad has at the same time an applicative functor +instance. Given a monad morphism $\phi:M\leadsto N$ between two monads, +show that $\phi$ is also an applicative morphism between applicative +functors $M$ and $N$. + +\textbf{(b)} Show that the converse does not hold: if $\phi:M\leadsto N$ +is an applicative morphism between two monads then $\phi$ is \emph{not} +necessarily a monad morphism. One example is where $M$ and $N$ are +\lstinline!State! monads {*}{*}{*}??? \subsubsection{Exercise \label{subsec:Exercise-traversables-10-3}\ref{subsec:Exercise-traversables-10-3}} @@ -4129,7 +4089,294 @@ \subsubsection{Exercise \label{subsec:Exercise-traversables-10-3-1}\ref{subsec:E \section{Discussion and further developments} -\subsection{Traversable contrafunctors or profunctors are not useful} +\subsection{Laws of \texttt{traverse} and properties of \texttt{zipWithIndex}\label{subsec:Laws-of-traverse-and-zipWithIndex}} + +Sections~\ref{subsec:Decorating-a-tree1}\textendash \ref{subsec:Decorating-a-tree-breadth-first-traversal} +defined the method \lstinline!zipWithIndex! for different orders +of tree traversal. In a similar way, we may define \lstinline!zipWithIndex! +(denoted $\text{zwi}_{L}$ for brevity) for any traversable functor +$L$. In this section, we will prove some intuitively reasonable properties +of \lstinline!zipWithIndex! by assuming only that the laws of \lstinline!traverse! +hold. This will serve as an additional evidence that the laws of \lstinline!traverse! +correspond to a programmer\textsf{'}s intuitions about code. + +To implement \lstinline!zipWithIndex! for $L$, we use $L$\textsf{'}s \lstinline!traverse! +method ($\text{trav}_{L}^{F,A,B}$) and chose the applicative functor +$F$ as the \lstinline!State! monad with internal state of type \lstinline!Int!: +\[ +F^{A}\triangleq\text{State}^{\text{Int},A}\triangleq\text{Int}\rightarrow A\times\text{Int}\quad. +\] +The internal state represents a current value of the index. The code +of \lstinline!zipWithIndex! applies \lstinline!traverse! to a function +of type $A\rightarrow\text{State}^{S,A\times\text{Int}}$ that increments +the index: +\begin{equation} +\text{zwi}_{L}^{A}:L^{A}\rightarrow L^{A\times\text{Int}}\quad,\quad\text{zwi}_{L}^{A}\triangleq\text{trav}_{L}^{F,A,A\times\text{Int}}(a^{:A}\rightarrow s^{:\text{Int}}\rightarrow(a\times s)\times(s+1))\bef\text{run}_{\text{State}}(0^{:\text{Int}})\quad,\label{eq:definition-of-zwi} +\end{equation} +Here $\text{run}_{\text{State}}(0^{:\text{Int}})$ is the \lstinline!State! +monad\textsf{'}s runner defined by Eq.~(\ref{eq:definition-of-runState}) +and applied to the zero integer value $0^{:\text{Int}}$: +\[ +\text{run}_{\text{State}}:S\rightarrow\text{State}^{S,A}\rightarrow A\quad,\quad\quad\text{run}_{\text{State}}(s_{0})\triangleq k^{:S\rightarrow A\times S}\rightarrow s_{0}\triangleright k\triangleright\pi_{1}\quad. +\] + +What are a programmer\textsf{'}s intuitive expectations about \lstinline!zipWithIndex! +when applied to arbitrary traversable functors $L$? First, \lstinline!zipWithIndex! +applied to a value $p:L^{A}$ should produce a value of type $L^{A\times\text{Int}}$ +that preserves the structure of $p$ and just adds indices at places +where some data of type $A$ is stored within $p$. Second, \lstinline!zipWithIndex! +should produce a \emph{different} index for every value of type $A$ +stored within $p$. Let us now formulate those intuitions as equations +that rigorously express the expected properties of \lstinline!zipWithIndex!. + +The first property is that the value $p:L^{A}$ must be restored if +we remove the index values: + +\begin{wrapfigure}{l}{0.5\columnwidth}% +\vspace{-1\baselineskip} +\begin{lstlisting} +p.zipWithIndex.map(_._1) == p +\end{lstlisting} +\vspace{-0.5\baselineskip} +\end{wrapfigure}% + +~\vspace{-0.5\baselineskip} +\[ +p\triangleright\text{zwi}_{L}\triangleright\pi_{1}^{\uparrow L}=p\quad. +\] + +The second property means that each index can be mapped to a distinct +value of type $A$ stored within $p$. Begin by computing a value +$q\triangleq p\triangleright\text{zwi}_{L}\triangleright\pi_{2}^{\uparrow L}$ +of type $L^{\text{Int}}$. Then $q$ has the same structure as $p$ +but carries integer index values instead of values of type $A$. We +expect that the original data ($p$) can be restored if we replace +the index values in $q$ by the corresponding values of type $A$. +In other words, there should exist a function $f:\text{Int}\rightarrow A$ +such that $p$ can be restored from $f$ and $q$ as $p=q\triangleright f^{\uparrow L}$. +This also means that there should be an injective map from the type +$L^{A}$ to the type $(\text{Int}\rightarrow A)\times L^{\text{Int}}$. + +We will now prove the first property of \lstinline!zipWithIndex!, +assuming only that $L$ is a lawful traversable functor. In that proof, +we will need a special subtype of the \lstinline!State! monad: + +\subsubsection{Statement \label{subsec:Statement-constant-value-state-monad}\ref{subsec:Statement-constant-value-state-monad}} + +We define a \textsf{``}constant-function \lstinline!State! monad\textsf{''}, denoted +by $\text{CFState}^{S,A}$, like this: Write the type of a \lstinline!State! +monad equivalently as $\text{State}^{S,A}\triangleq S\rightarrow A\times S\cong(S\rightarrow A)\times(S\rightarrow S)$ +and consider values of that type, $p^{:S\rightarrow A}\times q^{:S\rightarrow S}$, +such that $p^{:S\rightarrow A}$ is a\emph{ constant} function (independent +of its argument). In other words, the wrapped value of type $A$ is +independent of the state value of type $S$. This defines a subset +of all possible values of type $\text{State}^{S,A}$. We call that +subset $\text{CFState}^{S,A}$ and write: +\[ +\text{CFState}^{S,A}\triangleq(\_^{:S}\rightarrow A)\times(S\rightarrow S)\quad. +\] +This is a subtype of $\text{State}^{S,A}$ because there is a function +\lstinline!fromCF! of type $\text{CFState}^{S,A}\rightarrow\text{State}^{S,A}$ +that is an identity function that merely reassigns types: +\[ +\text{fromCF}:(\_^{:S}\rightarrow A)\times(S\rightarrow S)\rightarrow(S\rightarrow A)\times(S\rightarrow S)\quad,\quad\quad\text{fromCF}\triangleq p^{:\_\rightarrow A}\times q^{:S\rightarrow S}\rightarrow p^{:S\rightarrow A}\times q^{:S\rightarrow S}\quad. +\] +Assuming that $S$ is not a void type, we then have the following +properties: + +\textbf{(a)} The type $\text{CFState}^{S,A}$ is a monad with the +same implementation code as $\text{State}^{S,A}$. + +\textbf{(b)} The function \lstinline!fromCF! is an injective monad +morphism and applicative morphism. + +\textbf{(c)} The monad \lstinline!CFState! has a runner, $\text{run}_{\text{CFState}}:\text{CFState}^{S,A}\rightarrow A$, +defined by: +\[ +\text{run}_{\text{CFState}}\triangleq\text{fromCF}\bef\text{run}_{\text{State}}(s_{0})\quad, +\] +where $s_{0}^{:S}$ is a fixed value. The function $\text{run}_{\text{CFState}}$ +is the same for all choices of $s_{0}$ and is both a monad morphism +and an applicative morphism. (Note that $\text{run}_{\text{State}}(s_{0})$ +is neither a monad morphism nor an applicative morphism of type $\text{State}^{S,A}\rightarrow A$.) + +\textbf{(d)} The monad \lstinline!CFState! is equivalent to a \lstinline!Writer! +monad. There exists a one-to-one (bijective) monad morphism $\text{CFState}^{S,A}\rightarrow\text{Writer}^{W,A}$ +where $\text{Writer}^{W,A}\triangleq A\times W$ and the type $W\triangleq S\rightarrow S$ +is a function composition monoid (denoted by $\text{MF}^{S}$ in the +proof of Statement~\ref{subsec:Statement-foldleft-foldmap-equivalence}). + +\subparagraph{Proof} + +\textbf{(a)} Rewrite the code of \lstinline!State! monad\textsf{'}s methods +$\text{pu}_{\text{State}}$ and $\text{ftn}_{\text{State}}$ using +the equivalent type signature $\text{State}^{S,A}\cong(S\rightarrow A)\times(S\rightarrow S)$: +\begin{align*} + & \text{pu}_{\text{State}}:A\rightarrow(S\rightarrow A)\times(S\rightarrow S)\quad,\quad\quad\text{pu}_{\text{State}}=a^{:A}\rightarrow(\_^{:S}\rightarrow a)\times\text{id}^{:S\rightarrow S}\quad,\\ + & \text{ftn}_{\text{State}}:(S\rightarrow(S\rightarrow A)\times(S\rightarrow S))\times(S\rightarrow S)\rightarrow(S\rightarrow A)\times(S\rightarrow S)\quad,\\ + & \text{ftn}_{\text{State}}=p^{:S\rightarrow(S\rightarrow A)\times(S\rightarrow S)}\times q^{:S\rightarrow S}\rightarrow(s^{:S}\rightarrow s\triangleright q\triangleright(s\triangleright p\triangleright\pi_{1}))\times(s^{:S}\rightarrow s\triangleright q\triangleright(s\triangleright p\triangleright\pi_{2}))\quad. +\end{align*} +The code of $\text{pu}_{\text{State}}$ is already of type $A\rightarrow\text{CFState}^{S,A}$ +because $\text{pu}_{\text{State}}(a)$ returns a \lstinline!State! +monad containing a constant function ($\_^{:S}\rightarrow A$). It +remains to check that $\text{ftn}_{\text{State}}$ returns values +of the subtype $\text{CFState}^{S,A}$ when applied to a value of +type $\text{CFState}^{S,\text{CFState}^{S,A}}$: +\begin{align*} + & \text{ftn}_{\text{State}}(p^{:\_^{:S}\rightarrow(\_^{:S}\rightarrow A)\times(S\rightarrow S)}\times q^{:S\rightarrow S})\\ + & =(s^{:S}\rightarrow s\triangleright q\triangleright(\gunderline{s\triangleright p}\triangleright\pi_{1}))\times(s^{:S}\rightarrow...)\\ +{\color{greenunder}\text{denote }p_{0}\triangleq s\triangleright p:}\quad & =(s^{:S}\rightarrow\gunderline{s\triangleright q\triangleright\,}(p_{0}\triangleright\pi_{1}))\times(s^{:S}\rightarrow...)\\ +{\color{greenunder}\text{denote }a_{0}\triangleq s\triangleright q\triangleright(p_{0}\triangleright\pi_{1}):}\quad & =(s^{:S}\rightarrow a_{0})\times(s^{:S}\rightarrow...)=(\_^{:S}\rightarrow a_{0})\times(s^{:S}\rightarrow...)\quad. +\end{align*} +Here the constants $p_{0}:(\_^{:S}\rightarrow A)\times(S\rightarrow S)$ +and $a_{0}:A$ exist due to the assumption that both $p$ and $p_{0}\triangleright\pi_{1}$ +are constant functions. It follows that the code of $\text{pu}_{\text{State}}$ +and $\text{ftn}_{\text{State}}$ can be reused as $\text{pu}_{\text{CFState}}$ +and $\text{ftn}_{\text{CFState}}$ when restricted to the type $\text{CFState}^{S,A}$. +The monad laws hold for $\text{CFState}^{S,A}$ because those laws +hold for the code of $\text{pu}_{\text{State}}$ and $\text{ftn}_{\text{State}}$. + +\textbf{(b)} The code of the function \lstinline!fromCF! is an identity +function that only reassigns types. This indicates that \lstinline!CFState! +is a subtype of \lstinline!State! (in the sense of subtyping explained +in Section~\ref{subsec:Covariance,-contravariance,-and-subtyping}). + +The code of $\text{pu}_{\text{CFState}}$ and $\text{ftn}_{\text{CFState}}$ +is the same as the code of $\text{pu}_{\text{State}}$ and $\text{ftn}_{\text{State}}$ +respectively. So, \lstinline!fromCF! automatically satisfies the +laws of the monad morphism of type $\text{CFState}^{S,A}\rightarrow\text{State}^{S,A}$. + +The function \lstinline!fromCF! is then also an applicative morphism +(Exercise~\ref{subsec:Exercise-traversables-10-1-1-1}). + +To prove that \lstinline!fromCF! is injective, we will show that +it has a left inverse (denoted \lstinline!toCF!): +\[ +\text{toCF}:\text{State}^{S,A}\rightarrow\text{CFState}^{S,A}\quad,\quad\quad\text{toCF}\triangleq p^{:S\rightarrow A}\times q^{:S\rightarrow S}\rightarrow(\_^{:S}\rightarrow p(s_{0}))\times q\quad. +\] +Here $s_{0}$ is an arbitrary value of type $S$. That value exists +because $S$ is not a void type. + +It remains to verify that $\text{fromCF}\bef\text{toCF}=\text{id}$. +For any constant function $p\triangleq\_^{:S}\rightarrow a_{0}$ we +will have $p(s_{0})=a_{0}$ and so we can write: +\[ +\text{fromCF}\bef\text{toCF}=p^{:\_^{:S}\rightarrow A}\times q^{:S\rightarrow S}\rightarrow(\_^{:S}\rightarrow p(s_{0}))\times q=p\times q\rightarrow(\_\rightarrow a_{0})\times q=p\times q\rightarrow p\times q=\text{id}\quad. +\] + +\textbf{(c)} The code {*}{*}{*} + +\textbf{(d)} A value of type $\text{CFState}^{S,A}=(\_^{:S}\rightarrow A)\times(S\rightarrow S)$ +is a pair of a constant function and a value of type $W$. When $S$ +is not void, the type of constant functions $(\_^{:S}\rightarrow A)$ +is equivalent to the type $A$: we can substitute an arbitrary value +$s_{0}:S$ into a constant function and obtain the corresponding value +of type $A$. So, we have the type equivalence $\text{CFState}^{S,A}\cong A\times(S\rightarrow S)\triangleq A\times W$. +The isomorphism is given by a function \lstinline!inCF! defined by: +\[ +\text{inCF}:\text{Writer}^{W,A}\rightarrow\text{CFState}^{S,A}\quad,\quad\quad\text{inCF}\triangleq a^{:A}\times w^{:S\rightarrow S}\rightarrow(\_^{:S}\rightarrow a)\times w\quad. +\] + +It remains to verify that \lstinline!inCF! is a monad morphism. To +verify the identity law: +\begin{align*} + & \text{pu}_{\text{Writer}}\bef\text{inCF}=(a^{:A}\rightarrow a\times\text{id})\bef(a\times w\rightarrow(\_\rightarrow a)\times w)\\ + & =a\rightarrow(\_\rightarrow a)\times\text{id}=\text{pu}_{\text{State}}=\text{pu}_{\text{CFState}}\quad. +\end{align*} +$\square$ + +We are now ready to prove the first property of \lstinline!zipWithIndex!. + +\subsubsection{Statement \label{subsec:Statement-properties-of-zipWithIndex}\ref{subsec:Statement-properties-of-zipWithIndex}} + +For any lawful traversable functor $L$, define the function \lstinline!zipWithIndex! +(denoted for brevity by $\text{zwi}_{L}$) via Eq.~(\ref{eq:definition-of-zwi}). +Then \lstinline!zipWithIndex! satisfies the equation: $\text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{id}^{:L^{A}\rightarrow L^{A}}$. + +\textbf{(b)} There exists a \textsf{``}tabulating\textsf{''} function (denoted by +$\text{tab}_{L}^{A}$): +\[ +\text{tab}_{L}^{A}:L^{A\times\text{Int}}\rightarrow(\text{Int}\rightarrow A)\times L^{\text{Int}}\quad, +\] +such that $\text{zwi}_{L}\bef\text{tab}_{L}\bef(f^{:\text{Int}\rightarrow A}\times q^{:L^{\text{Int}}}\rightarrow q\triangleright f^{\uparrow L})=\text{id}^{:L^{A}\rightarrow L^{A}}$. +So, the function $\text{zwi}_{L}\bef\text{tab}_{L}$ is an injective +map of type $L^{A}\rightarrow(\text{Int}\rightarrow A)\times L^{\text{Int}}$. + +\subparagraph{Proof} + +To make the definition~(\ref{eq:definition-of-zwi}) easier to work +with, we denote: +\begin{align*} + & F^{A}\triangleq\text{State}^{\text{Int},A}=(\text{Int}\rightarrow A)\times(\text{Int}\rightarrow\text{Int})\quad,\\ + & g:A\rightarrow\text{State}^{\text{Int},A\times\text{Int}}\quad,\quad\quad g\triangleq a^{:A}\rightarrow(s^{:\text{Int}}\rightarrow a\times s)\times(s^{:\text{Int}}\rightarrow s+1)\quad,\\ + & r:\forall B.\,\text{State}^{\text{Int},B}\rightarrow B\quad,\quad\quad r\triangleq p^{:\text{Int}\rightarrow B}\times q^{:\text{Int}\rightarrow\text{Int}}\rightarrow p(0^{:\text{Int}})\quad. +\end{align*} +and get $\text{zwi}_{L}=\text{trav}_{L}^{F,A,A\times\text{Int}}(g)\bef r$. +Then we write: +\begin{align*} + & \text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{trav}_{L}^{F,A,A\times\text{Int}}(g)\bef\gunderline{r\bef\pi_{1}^{\uparrow L}}\\ +{\color{greenunder}\text{naturality of }r:}\quad & =\gunderline{\text{trav}_{L}^{F,A,A\times\text{Int}}(g)\bef\pi_{1}^{\uparrow L\uparrow\text{State}}}\bef r\\ +{\color{greenunder}\text{naturality of }\text{trav}_{L}:}\quad & =\text{trav}_{L}^{F,A,A}(g\bef\pi_{1}^{\uparrow\text{State}})\bef r\quad. +\end{align*} +To compute $g\bef\pi_{1}^{\uparrow\text{State}}$, we first express +the lifting to \lstinline!State! as: +\[ +(f^{:A\rightarrow B})^{\uparrow\text{State}^{S,\bullet}}(p^{:S\rightarrow A}\times q^{:S\rightarrow S})=(p\bef f)\times q\quad. +\] +Then we get: +\begin{align*} + & g\bef\pi_{1}^{\uparrow\text{State}}:A\rightarrow\text{State}^{\text{Int},A}\quad,\\ + & g\bef\pi_{1}^{\uparrow\text{State}}=a^{:A}\rightarrow(s^{:\text{Int}}\rightarrow\pi_{1}(a\times s))\times(s^{:\text{Int}}\rightarrow s+1)\\ + & =a\rightarrow(s^{:\text{Int}}\rightarrow a)\times(s^{:\text{Int}}\rightarrow s+1)=a\rightarrow(\_^{:\text{Int}}\rightarrow a)\times(s^{:\text{Int}}\rightarrow s+1)\quad. +\end{align*} +Now we note that the sub-expression ($\_^{:\text{Int}}\rightarrow a$) +is a constant function. So, $g\bef\pi_{1}^{\uparrow\text{State}}$ +can be expressed as a function $h$ of type $A\rightarrow\text{CFState}^{\text{Int},A}$ +followed by \lstinline!fromCF!: +\begin{align*} + & h:A\rightarrow\text{CFState}^{\text{Int},A}=A\rightarrow(\_^{:\text{Int}}\rightarrow A)\times(\text{Int}\rightarrow\text{Int})\quad,\\ + & h\triangleq a\rightarrow(\_^{:\text{Int}}\rightarrow a)\times(s^{:\text{Int}}\rightarrow s+1)\quad,\quad\quad g\bef\pi_{1}^{\uparrow\text{State}}=h\bef\text{fromCF}\quad. +\end{align*} + +In this way, we have found that: +\[ +\text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{trav}_{L}^{\text{State}^{\text{Int},\bullet},A,A}(h\bef\text{fromCF})\bef r\quad. +\] +Since \lstinline!fromCF! is an applicative morphism, we can use the +applicative naturality law~(\ref{eq:traverse-applicative-naturality-law}) +with $B\triangleq A$, $F^{A}\triangleq\text{CFState}^{\text{Int},A}$, +$G^{A}\triangleq\text{State}^{\text{Int},A}$, $f^{:F^{B}\rightarrow G^{B}}=$ +\lstinline!fromCF!, and $g^{:A\rightarrow F^{B}}=h$: +\[ +\text{trav}_{L}^{G,A,B}(g\bef f)=\text{trav}_{L}^{F,A,B}(g)\bef f\quad\quad\text{or equivalently:}\quad\quad\text{trav}_{L}(h\bef\text{fromCF})=\text{trav}_{L}(h)\bef\text{fromCF}\quad. +\] +Statement~\ref{subsec:Statement-constant-value-state-monad}(c) gives +the formula $\text{run}_{\text{CFState}}=\text{fromCF}\bef r$, so +we write: +\[ +\text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{trav}_{L}(h)\bef\gunderline{\text{fromCF}\bef r}=\text{trav}_{L}(h)\bef\text{run}_{\text{CFState}}\quad. +\] +As $\text{run}_{\text{CFState}}$ is an applicative morphism, we may +again use the applicative naturality law: +\[ +\text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{trav}_{L}(h)\bef\text{run}_{\text{CFState}}=\text{trav}_{L}(h\bef\text{run}_{\text{CFState}})\quad. +\] +Now we recall the identity law~(\ref{eq:traverse-identity-law}) +and compute: +\begin{align*} + & h\bef\text{run}_{\text{CFState}}=a\rightarrow(\_^{:\text{Int}}\rightarrow a)\times(s^{:\text{Int}}\rightarrow s+1)\bef\text{run}_{\text{CFState}}=a\rightarrow a=\text{id}^{:A\rightarrow A}\quad,\\ + & \text{zwi}_{L}\bef\pi_{1}^{\uparrow L}=\text{trav}_{L}(h\bef\text{run}_{\text{CFState}})=\text{trav}_{L}(\text{id})=\text{id}\quad. +\end{align*} + $\square$ + +The second property of \lstinline!zipWithIndex! does \emph{not} seem +to be provable using the laws of traverse; see Problem~\ref{par:Problem-traverse-law}(b). +However, it appears to be natural to expect that \lstinline!zipWithIndex! +assigns different indices to each value of type $A$ stored inside +a data structure of type $L^{A}$. Perhaps another law of \lstinline!traverse! +is needed? + +\subsection{Traversable contrafunctors and profunctors are not useful} In Chapter~\ref{chap:8-Applicative-functors,-contrafunctors}, we found some uses for applicative contrafunctors and profunctors. We @@ -4157,30 +4404,31 @@ \subsection{Traversable contrafunctors or profunctors are not useful} \[ \text{seq}_{L}^{F,A}=\text{pu}_{F}^{\downarrow L}\bef\text{pu}_{F}\quad. \] -The implementation of $\text{seq}_{L}$ follows unambiguously from -the applicative naturality law. +No other implementation of $\text{seq}_{L}$ would obey the applicative +naturality law and the identity law. -We can now verify the composition law~(\ref{eq:composition-law-of-sequence}): +We can verify that the composition law~(\ref{eq:composition-law-of-sequence}) +holds for this $\text{seq}_{L}$: \begin{align*} {\color{greenunder}\text{left-hand side}:}\quad & \text{seq}_{L}^{F,G^{A}}\bef(\text{seq}_{L}^{G,A})^{\uparrow F}=\text{pu}_{F}^{\downarrow L}\bef\gunderline{\text{pu}_{F}\bef(\text{pu}_{G}^{\downarrow L}\bef\text{pu}_{G})^{\uparrow F}}\\ {\color{greenunder}\text{naturality law of }\text{pu}_{F}:}\quad & \quad=\gunderline{\text{pu}_{F}^{\downarrow L}\bef\text{pu}_{G}^{\downarrow L}}\bef\text{pu}_{G}\bef\text{pu}_{F}=(\text{pu}_{G}\bef\text{pu}_{F})^{\downarrow L}\bef\text{pu}_{G}\bef\text{pu}_{F}\quad,\\ {\color{greenunder}\text{right-hand side}:}\quad & \text{seq}_{L}^{F\circ G,A}=\text{pu}_{F\circ G}^{\downarrow L}\bef\text{pu}_{F\circ G}=(\text{pu}_{G}\bef\text{pu}_{F})^{\downarrow L}\bef\text{pu}_{G}\bef\text{pu}_{F}\quad. \end{align*} -So, every contrafunctor\textsf{'}s \lstinline!sequence! method satisfies the -laws of traversables. -However, the code of $\text{seq}_{L}^{F,A}$ always produces values -with empty $F$-effects. Any information about nontrivial $F$-effects -is ignored. For example, if $L^{A}\triangleq A\rightarrow Z$ (where -$Z$ is a fixed type) then: +So, every contrafunctor has a unique \lstinline!sequence! method +that obeys the laws of traversables. However, the code of \lstinline!sequence! +ignores all $F$-effects in its arguments and always produces values +with empty $F$-effects. For example, if $L^{A}\triangleq A\rightarrow Z$ +(where $Z$ is a fixed type) then: \[ \text{seq}_{L}^{F,A}:(F^{A}\rightarrow Z)\rightarrow F^{A\rightarrow Z}\quad,\quad\quad\text{seq}_{L}^{F,A}(f^{:F^{A}\rightarrow Z})=\text{pu}_{F}(a^{:A}\rightarrow f(\text{pu}_{F}(a)))\quad. \] The function $f$ will be never applied to nontrivial $F$-effects. So, the function $\text{seq}_{L}$ will never obtain any information -that $f$ would return when applied to nontrivial $F$-effects. We -conclude that contrafunctors are traversable in a way that does not -seem to be practically useful. +that $f$ would return when applied to nontrivial $F$-effects. + +We see that contrafunctors are traversable in a way that is not practically +useful. Turning now to the case where $L$ is a profunctor, consider a simple example: @@ -4194,9 +4442,7 @@ \subsection{Traversable contrafunctors or profunctors are not useful} \[ \text{seq}_{L}\triangleq p^{:F^{A}\rightarrow F^{A}}\rightarrow\text{pu}_{F}(\text{id}^{:A\rightarrow A})\quad. \] -This implementation ignores its argument $p$, losing information -and always returning an empty $F$-effect. As with contrafunctors, -we find that profunctors are not traversable in a useful way. +This function is not useful because it ignores its argument $p$. \subsection{Traversals for nested recursive types} @@ -4252,11 +4498,9 @@ \subsubsection{Statement \label{subsec:Statement-nested-recursive-type-traversab \[ \text{seq}_{L}^{F,A}:S^{F^{A},L^{P^{F^{A}}}}\rightarrow F^{S^{A,L^{P^{A}}}}\quad,\quad\quad\text{seq}_{L}^{F,A}\triangleq\big(\text{seq}_{P}^{F,A}\big)^{\uparrow L\uparrow S^{F^{A},\bullet}}\bef\big(\overline{\text{seq}}_{L}^{F,P^{A}}\big)^{\uparrow S^{F^{A},\bullet}}\bef\text{seq2}_{S}^{F,A,L^{P^{A}}}\quad. \] -\vspace{-1\baselineskip} \[ \xymatrix{\xyScaleY{1.4pc}\xyScaleX{6pc}S^{F^{A},L^{P^{F^{A}}}}\ar[r]\sp(0.5){\big(\text{seq}_{P}^{F,A}\big)^{\uparrow L\uparrow S^{F^{A},\bullet}}} & S^{F^{A},L^{F^{P^{A}}}}\ar[r]\sp(0.5){\big(\overline{\text{seq}}_{L}^{F,P^{A}}\big)^{\uparrow S^{F^{A},\bullet}}} & S^{F^{A},F^{L^{P^{A}}}}\ar[r]\sp(0.5){\text{seq2}_{S}^{F,A,L^{P^{A}}}} & F^{S^{A,L^{P^{A}}}}} \] -\vspace{-1\baselineskip} \subparagraph{Proof} diff --git a/sofp-src/sofp-typeclasses.lyx b/sofp-src/sofp-typeclasses.lyx index b2b5024ae..232a24919 100644 --- a/sofp-src/sofp-typeclasses.lyx +++ b/sofp-src/sofp-typeclasses.lyx @@ -24,6 +24,9 @@ % No page numbers on "Part" pages. \renewcommand*{\partpagestyle}{empty} +% Use a special "equal by definition" symbol. +\renewcommand*{\triangleq}{\overset{\lower1mm\hbox{\texttt{\tiny def}}} {=}} + % Running head: works, but results are not satisfactory. %\usepackage{scrlayer-scrpage} %\automark[subsection]{chapter} @@ -175,8 +178,20 @@ % Better text quotes. \renewcommand\textquotedblleft{``} \renewcommand\textquotedblright{''} + +% Better symbol for the pair mapper instead of \ogreaterthan and \varogreaterthan. +\newcommand{\boxrightarrow}{\mathbin{\ensuremath{% +\mathchoice% + {\displaystyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\boxminus\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\textstyle{\boxminus}\kern-5.35pt\raisebox{0.75pt}{$\scriptstyle{\succ}$}}% + {\scriptstyle{\boxminus}\kern-3.7pt\raisebox{0.49pt}{$\scriptscriptstyle{\succ}$}}% +}% end of mathchoice with raisebox +\hspace{1.0pt}}} +\renewcommand{\ogreaterthan}{\boxrightarrow} +\renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \master sofp.lyx \maintain_unincluded_children false @@ -246,12 +261,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -585,21 +600,6 @@ F \begin_layout Standard What would that constraint be like? Consider an ordinary function with no type parameters, e.g.: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement L -overhang 0in -width "28col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -611,23 +611,6 @@ def f(x: Int): Int = x + 1 \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -95baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In this code, the syntax \begin_inset listings inline true @@ -1382,21 +1365,6 @@ value-dependent types ). An example in Scala: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "36col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1413,23 +1381,6 @@ val y: x.T = 123 \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In this example, \begin_inset listings inline true @@ -1633,21 +1584,6 @@ Right(...) will cause a run-time error. However, consider this code: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1681,23 +1617,6 @@ res0: Seq[Int] = List(0, 1) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent Although \begin_inset listings inline true @@ -1822,21 +1741,6 @@ p \end_inset , safe to use: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "45col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -1853,23 +1757,6 @@ res1: Seq[Int] = List(0, 1) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The \begin_inset listings inline true @@ -2105,22 +1992,7 @@ noprefix "false" ), which are type constructors whose type parameters are restricted to specific types. - As an example of an unfunctor, consider the code shown -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "54col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -60baselineskip% -\end_inset - - + As an example of an unfunctor, consider this code: \begin_inset listings inline false status open @@ -2142,24 +2014,7 @@ final case class FracD() extends Frac[Double] \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -at left, where values of type +Values of type \begin_inset listings inline true status open @@ -2254,21 +2109,6 @@ Frac[Boolean] . The Scala compiler will not detect any errors in the following code: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "32col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -2285,23 +2125,6 @@ type U = Frac[Boolean] \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent But we will never be able to create and use any values of types \begin_inset listings inline true @@ -2372,21 +2195,6 @@ void type types. Trying to create and use values of these types will result in type errors, as the following code shows: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "46col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -95baselineskip% -\end_inset - - \begin_inset listings lstparams "numbers=left" inline false @@ -2414,23 +2222,6 @@ y match { case FracD() => } // Type error. \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -105baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In line \begin_inset space ~ \end_inset @@ -3388,21 +3179,6 @@ trait \end_inset with methods requires this code: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "59.5col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -65baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -3469,23 +3245,6 @@ val fracD = new Frac[Double] { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The function \begin_inset listings inline true @@ -5270,12 +5029,12 @@ If the helper class is defined in some library, the programmer will have \end_layout \begin_layout Subsection -Solved examples: Implementing typeclasses in practice +Examples: Implementing typeclasses in practice \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -6006,21 +5765,6 @@ implicit class ExtractorsSyntax[T: HasMetadata](t: T) { \end_inset With this definition, we can write: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "34col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -85baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -6059,23 +5803,6 @@ res4: Long = 200 \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -95baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The code looks as if the new methods \begin_inset listings inline true @@ -9024,21 +8751,6 @@ F \end_inset constrained to be a functor: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "65col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -5baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -9050,20 +8762,6 @@ def inject[F[_]: Functor, A, B](a: A, f: F[B]): F[(A, B)] = ??? \end_inset - -\begin_inset VSpace -85baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The \begin_inset listings inline true @@ -9132,21 +8830,6 @@ map \end_inset function with the standard type signature: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "43col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -9158,25 +8841,7 @@ def map[A, B](fa: F[A])(f: A => B): F[B] \end_inset - -\begin_inset VSpace -5baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent In the type notation, this type signature is written as: -\begin_inset VSpace -45baselineskip% -\end_inset - - \begin_inset Formula \[ \text{map}:\forall(A,B).\,F^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow F^{B}\quad. @@ -9184,15 +8849,6 @@ In the type notation, this type signature is written as: \end_inset - -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent So, a typeclass instance of the \begin_inset listings inline true @@ -9289,21 +8945,6 @@ Functor \end_inset -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "43col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -9325,20 +8966,6 @@ trait Functor[F[_]] { \end_inset - -\begin_inset VSpace -75baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The type constructor \begin_inset listings inline true @@ -13254,38 +12881,7 @@ Here we assume that the type \end_inset can be computed from scratch. - -\begin_inset Note Note -status open - -\begin_layout Plain Layout -(This would not be the case when -\begin_inset Formula $R=\bbnum 1$ -\end_inset - - or -\begin_inset Formula $R=\bbnum 2$ -\end_inset - -, say. - But in those cases the type -\begin_inset Formula $R\rightarrow A$ -\end_inset - - can be simplified to a polynomial type, e.g., -\begin_inset Formula $\bbnum 1\rightarrow A\cong A$ -\end_inset - - and -\begin_inset Formula $\bbnum 2\rightarrow A\cong A\times A$ -\end_inset - -, etc.) -\end_layout - -\end_inset - -Without values of type + Without values of type \begin_inset Formula $R$ \end_inset @@ -17805,48 +17401,10 @@ Monoid \end_inset typeclass. - The full code is shown in Figure -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:Implementation-of-Monoid-typeclass-with-trait" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -. -\end_layout - -\begin_layout Standard -The monoid laws hold for the recursive instances by induction (see Section -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:The-Eq-typeclass" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -). + The full code is: \end_layout \begin_layout Standard -\begin_inset Float figure -wide false -sideways false -status open - -\begin_layout Plain Layout \begin_inset listings lstparams "frame=single,fillcolor={\color{black}},framesep={0.2mm},framexleftmargin=2mm,framexrightmargin=2mm,framextopmargin=2mm,framexbottommargin=2mm" inline false @@ -18172,53 +17730,22 @@ res0: T = T(Right((abcabc,))) \end_layout -\begin_layout Plain Layout -\begin_inset Caption Standard - -\begin_layout Plain Layout -Implementing a recursive instance of the -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Monoid -\end_layout - -\end_inset - - typeclass via a -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -trait -\end_layout - -\end_inset - -. - -\begin_inset CommandInset label -LatexCommand label -name "fig:Implementation-of-Monoid-typeclass-with-trait" - -\end_inset - - -\end_layout - +\begin_layout Standard +The monoid laws hold for the recursive instances by induction (see Section +\begin_inset space ~ \end_inset -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:The-Eq-typeclass" +plural "false" +caps "false" +noprefix "false" \end_inset - +). \end_layout \begin_layout Paragraph @@ -18753,21 +18280,6 @@ pure \end_inset in Scala libraries, is implemented as a function with a type signature: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -18779,20 +18291,6 @@ def pure[A]: A => F[A] \end_inset - -\begin_inset VSpace -80baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent The code notation for this function is \begin_inset Formula $\text{pu}_{F}$ \end_inset @@ -19082,21 +18580,6 @@ pure \end_inset can be defined and used like this: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "41col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -95baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -19127,20 +18610,6 @@ res0: Option[Int] = Some(123) \end_inset - -\begin_inset VSpace -95baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent When a pointed type constructor \begin_inset Formula $F$ \end_inset @@ -19278,21 +18747,6 @@ map \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "41col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings lstparams "mathescape=true" inline false @@ -19317,20 +18771,6 @@ uparrow F}}$ \end_inset - -\begin_inset VSpace -80baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent We expect that the result should be the same as a wrapped \begin_inset Formula $f(x)$ \end_inset @@ -19451,22 +18891,6 @@ In the \end_inset -\end_layout - -\begin_layout Standard -\noindent -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "28col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -420baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{2.0pc}\xyScaleX{4.0pc}A\ar[r]\sp(0.5){\text{pu}_{F}}\ar[d]\sb(0.45){f} & F^{A}\ar[d]\sp(0.45){f^{\uparrow F}}\\ @@ -19476,28 +18900,6 @@ B\ar[r]\sp(0.5){\text{pu}_{F}} & F^{B} \end_inset - -\begin_inset VSpace -10baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -\begin_inset VSpace -190baselineskip% -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent This motivates the following definition: A functor \begin_inset Formula $F^{\bullet}$ \end_inset @@ -19793,21 +19195,6 @@ wrapped unit \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "46col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -19819,24 +19206,7 @@ def pure[A](x: A): F[A] = wu.map { _ => x } \end_inset - -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -\begin_inset VSpace -100baselineskip% -\end_inset - - +In code notation: \begin_inset Formula \begin{equation} \text{pu}_{F}^{:A\rightarrow F^{A}}\triangleq x^{:A}\rightarrow\text{wu}_{F}\triangleright(\_\rightarrow x)^{\uparrow F}\quad.\label{eq:pu-via-wu-def} @@ -21608,21 +20978,6 @@ Pointed \end_inset is implemented by the following code: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "38col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -21650,19 +21005,9 @@ implicit val pointedF: Pointed[F] = \end_inset -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\noindent The corresponding \begin_inset listings inline true @@ -22409,21 +21754,6 @@ noprefix "false" \end_inset -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "25col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -420baselineskip% -\end_inset - - \begin_inset Formula \[ \xymatrix{\xyScaleY{2.0pc}\xyScaleX{4.0pc}F^{A}\ar[r]\sp(0.5){\text{ex}_{F}}\ar[d]\sb(0.45){f^{\uparrow F}} & A\ar[d]\sp(0.45){~f}\\ @@ -22434,22 +21764,9 @@ F^{B}\ar[r]\sp(0.5){\text{ex}_{F}} & B \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -150baselineskip% -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Standard -\noindent The naturality law formulates our expectation that the extractor function somehow \begin_inset Quotes eld @@ -23070,7 +22387,7 @@ For co-pointed functors lines 0 placement l overhang 0in -width "62col%" +width "56col%" status open \begin_layout Plain Layout @@ -23089,7 +22406,7 @@ def copointedEitherFG[F[_]: Copointed, G[_]: Copointed]: \begin_layout Plain Layout - Copointed[Lambda[X => Either[F[X],G[X]]]] = + Copointed[Lambda[X => Either[F[X],G[X]]]] = \end_layout \begin_layout Plain Layout @@ -23730,12 +23047,14 @@ def exS[A]: S[A, A] => A = { \end_layout \begin_layout Standard -\noindent -In the code notation, the function -\begin_inset Formula $\text{ex}_{S}$ +\begin_inset space ~ \end_inset - is written as: + +\end_layout + +\begin_layout Standard +\noindent \begin_inset VSpace -20baselineskip% \end_inset @@ -25443,12 +24762,12 @@ noprefix "false" \end_layout \begin_layout Subsection -Solved examples +Examples \begin_inset Index idx status open \begin_layout Plain Layout -solved examples +examples (with code) \end_layout \end_inset @@ -28689,7 +28008,7 @@ match lines 0 placement l overhang 0in -width "58col%" +width "55col%" status open \begin_layout Plain Layout @@ -28745,7 +28064,7 @@ def f[C[_]: Contrafunctor, A, B] \begin_layout Standard \noindent -The code notation for this function is: +In the code notation: \begin_inset VSpace -25baselineskip% \end_inset @@ -30718,22 +30037,7 @@ Russell O'Connor \begin_inset Formula $q$ \end_inset - with type signature -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "52col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - + with type signature: \begin_inset listings inline false status open @@ -30745,27 +30049,16 @@ def q[A, B, F[_]: Functor]: F[(A, B)] => (A, F[B]) \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -160baselineskip% -\end_inset - - -\end_layout - +or, in code notation, +\begin_inset Formula $q^{A,B}:F^{A\times B}\rightarrow A\times F^{B}$ \end_inset - -\end_layout - -\begin_layout Standard -\noindent -\begin_inset Formula $q^{A,B}:F^{A\times B}\rightarrow A\times F^{B}$ +. + Additionally, assume that +\begin_inset Formula $q$ \end_inset -, additionally satisfying the special laws of identity and associativity: + satisfies the following special laws of identity and associativity: \begin_inset Formula \[ q^{\bbnum 1,B}=f^{:F^{\bbnum 1\times B}}\rightarrow1\times(f\triangleright(1\times b^{:B}\rightarrow b)^{\uparrow F})\quad,\quad\quad q^{A,B\times C}\bef(\text{id}^{A}\boxtimes q^{B,C})=q^{A\times B,C}\quad. @@ -31226,21 +30519,6 @@ The next case is the identity functor . If we translate this type equation into Scala code, we will run into a problem with recursion: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "46col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -31267,23 +30545,6 @@ val x = T(x) // Infinite loop! \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -115baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent A value of \begin_inset listings inline true @@ -31413,21 +30674,6 @@ Next, consider a product functor such as \end_inset ? -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "46col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -80baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -31444,23 +30690,6 @@ val x = T(s = (x, x, 123)) // Infinite loop! \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent We again have an infinite loop when creating values of type \begin_inset listings inline true @@ -31533,21 +30762,6 @@ For some disjunctive type constructors \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "46col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -31569,23 +30783,6 @@ val y: T = T(Right(x, x)) // OK \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -100baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent We are able to create \begin_inset listings inline true @@ -32999,21 +32196,6 @@ To implement \end_inset . -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "40col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -33036,26 +32218,6 @@ def f1[A](arr: Array[A]): List[A] = \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -125baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -\begin_inset VSpace -125baselineskip% -\end_inset - - \begin_inset Formula \begin{align*} f_{1}(\text{Array}_{0}^{A}) & \triangleq\bbnum 1+\bbnum 0^{:A\times\text{List}^{A}}\quad,\\ @@ -33064,14 +32226,6 @@ f_{1}(g^{:\text{Int}_{[0,n]}\rightarrow A}) & \triangleq\bbnum 0+g(0)^{:A}\times \end_inset - -\begin_inset VSpace -50baselineskip% -\end_inset - - -\end_layout - -\begin_layout Standard To implement \begin_inset Formula $f_{2}$ \end_inset @@ -33173,21 +32327,6 @@ List[A] \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "43.63col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -33215,26 +32354,6 @@ def f2[A: ClassTag]: List[A] => Array[A] = \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -125baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -\begin_inset VSpace -125baselineskip% -\end_inset - - \begin_inset Formula \begin{align*} f_{2}(\bbnum 1+\bbnum 0^{:A\times\text{List}^{A}}) & \triangleq\text{Array}_{0}^{A}\quad,\\ @@ -33247,10 +32366,6 @@ i\geq1: & \overline{f_{2}}(s)(i-1)\quad. \end_inset -\begin_inset VSpace -50baselineskip% -\end_inset - - \end_layout \begin_layout Standard @@ -33394,21 +32509,6 @@ concat \end_inset function for lists is defined recursively as: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "38col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -33441,26 +32541,6 @@ def concat[A](p: List[A], q: List[A]) \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -250baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent -\begin_inset VSpace -90baselineskip% -\end_inset - - \begin_inset Formula \[ p^{:\text{List}^{A}}\pplus q^{:\text{List}^{A}}\triangleq p\triangleright\begin{array}{|c||c|} @@ -35405,30 +34485,7 @@ type relation!many-to-one \end_inset instead of many-to-many. - The resulting code is shown in Figure -\begin_inset space ~ -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "fig:Full-code-implementing-units-length-mass" -plural "false" -caps "false" -noprefix "false" - -\end_inset - -. -\end_layout - -\begin_layout Standard -\begin_inset Float figure -wide false -sideways false -status open - -\begin_layout Plain Layout + The resulting code is: \begin_inset listings lstparams "frame=single,fillcolor={\color{black}},framesep={0.2mm},framexleftmargin=2mm,framexrightmargin=2mm,framextopmargin=2mm,framexbottommargin=2mm" inline false @@ -35701,30 +34758,6 @@ res3: Quantity[FT] = Quantity(4.0) \end_inset -\end_layout - -\begin_layout Plain Layout -\begin_inset Caption Standard - -\begin_layout Plain Layout -Implementing type-safe computations with units of length and mass. -\begin_inset CommandInset label -LatexCommand label -name "fig:Full-code-implementing-units-length-mass" - -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\end_inset - - \end_layout \begin_layout Subsection @@ -36118,21 +35151,6 @@ T \end_inset : -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "48col%" -status open - -\begin_layout Plain Layout -\begin_inset VSpace -95baselineskip% -\end_inset - - \begin_inset listings inline false status open @@ -36159,23 +35177,6 @@ implicit val c: Monoid[Int] = new Monoid[Int] { \end_inset - -\end_layout - -\begin_layout Plain Layout -\begin_inset VSpace -110baselineskip% -\end_inset - - -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\noindent With this approach, we cannot avoid repeating the code for \begin_inset listings inline true @@ -36525,12 +35526,11 @@ status open \begin_layout Plain Layout \align center \begin_inset Tabular - + - \begin_inset Text @@ -36554,18 +35554,6 @@ Typeclass Type of instance values \end_layout -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\series bold -\size small -Inductive form -\end_layout - \end_inset @@ -36575,7 +35563,7 @@ Inductive form \series bold \size small -Structure functor +Inductive form \end_layout \end_inset @@ -36603,20 +35591,6 @@ pointed type \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $P^{A}\rightarrow A$ -\end_inset - - \end_layout \end_inset @@ -36627,7 +35601,7 @@ pointed type \begin_layout Plain Layout \size small -\begin_inset Formula $P^{A}\triangleq\bbnum 1$ +\begin_inset Formula $P^{A}\rightarrow A$ \end_inset @@ -36658,20 +35632,6 @@ semigroup \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $P^{A}\rightarrow A$ -\end_inset - - \end_layout \end_inset @@ -36682,7 +35642,7 @@ semigroup \begin_layout Plain Layout \size small -\begin_inset Formula $P^{A}\triangleq A\times A$ +\begin_inset Formula $P^{A}\rightarrow A$ \end_inset @@ -36713,20 +35673,6 @@ monoid \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $P^{A}\rightarrow A$ -\end_inset - - \end_layout \end_inset @@ -36737,7 +35683,7 @@ monoid \begin_layout Plain Layout \size small -\begin_inset Formula $P^{A}\triangleq\bbnum 1+A\times A$ +\begin_inset Formula $P^{A}\rightarrow A$ \end_inset @@ -36768,20 +35714,6 @@ functor \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$ -\end_inset - - \end_layout \end_inset @@ -36792,7 +35724,7 @@ functor \begin_layout Plain Layout \size small -\begin_inset Formula $P^{A,B,F}\triangleq F^{A}\times\left(A\rightarrow B\right)$ +\begin_inset Formula $\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$ \end_inset @@ -36823,20 +35755,6 @@ pointed functor \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$ -\end_inset - - \end_layout \end_inset @@ -36847,7 +35765,7 @@ pointed functor \begin_layout Plain Layout \size small -\begin_inset Formula $P^{A,B,F}\triangleq B+F^{A}\times\left(A\rightarrow B\right)$ +\begin_inset Formula $\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$ \end_inset @@ -36878,20 +35796,6 @@ contrafunctor \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$ -\end_inset - - \end_layout \end_inset @@ -36902,7 +35806,7 @@ contrafunctor \begin_layout Plain Layout \size small -\begin_inset Formula $P^{A,B,F}\triangleq F^{A}\times\left(B\rightarrow A\right)$ +\begin_inset Formula $\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$ \end_inset @@ -36933,20 +35837,6 @@ pointed contrafunctor \end_inset -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\size small -\begin_inset Formula $\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$ -\end_inset - - \end_layout \end_inset @@ -36957,7 +35847,7 @@ pointed contrafunctor \begin_layout Plain Layout \size small -\begin_inset Formula $P^{A,B,F}\triangleq\bbnum 1+F^{A}\times\left(B\rightarrow A\right)$ +\begin_inset Formula $\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$ \end_inset @@ -37307,19 +36197,9 @@ noprefix "false" \end_inset except for the additional type parameters. - -\begin_inset Note Note -status collapsed - -\begin_layout Plain Layout -The laws of type constructor typeclasses are also more complicated because + The laws of type constructor typeclasses are also more complicated because they often involve multiple type parameters and arbitrary functions. - -\end_layout - -\end_inset - -To simplify the presentation in this section, we will only consider typeclasses + To simplify the presentation in this section, we will only consider typeclasses for simple types, such as \begin_inset listings inline true diff --git a/sofp-src/sofp-typeclasses.tex b/sofp-src/sofp-typeclasses.tex index bfabe0cbf..41ddec3ed 100644 --- a/sofp-src/sofp-typeclasses.tex +++ b/sofp-src/sofp-typeclasses.tex @@ -49,19 +49,12 @@ \subsection{Constraining type parameters} What would that constraint be like? Consider an ordinary function with no type parameters, e.g.: - -\begin{wrapfigure}{L}{0.28\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} def f(x: Int): Int = x + 1 \end{lstlisting} - -\vspace{-0.95\baselineskip} -\end{wrapfigure}% - -\noindent In this code, the syntax \lstinline!x: Int! constrains -the value of the argument \lstinline!x! to be integer. It is a type -error to apply \lstinline!f! to a non-integer argument. +In this code, the syntax \lstinline!x: Int! constrains the value +of the argument \lstinline!x! to be integer. It is a type error to +apply \lstinline!f! to a non-integer argument. Using a similar syntax for \emph{type} \emph{parameters}, we write the type signatures for \lstinline!avg! and \lstinline!inject! as: @@ -143,20 +136,13 @@ \subsection{Functions of types and values} functions (VTFs) are known as \textbf{dependent} \textbf{types}\index{dependent type} (or, more verbosely, as \textsf{``}value-dependent types\textsf{''}). An example in Scala: - -\begin{wrapfigure}{l}{0.36\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} val x = new { type T = Int } val y: x.T = 123 \end{lstlisting} - -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -\noindent In this example, \lstinline!x.T! is a dependent type because -it is a type that depends on the \emph{value} \lstinline!x!. For -the value \lstinline!x! defined in this code, the expression \lstinline!x.T! +In this example, \lstinline!x.T! is a dependent type because it is +a type that depends on the \emph{value} \lstinline!x!. For the value +\lstinline!x! defined in this code, the expression \lstinline!x.T! evaluates to the type \lstinline!Int!. We will not consider dependent types (VTFs) in this chapter because @@ -180,20 +166,13 @@ \subsection{Partial functions of types and values} may be applied only to values of the form \lstinline!Left(...)!. Applying \lstinline!p! to a value \lstinline!Right(...)! will cause a run-time error. However, consider this code: - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-1\baselineskip} \begin{lstlisting} val x = Seq(Left(1), Right("abc"), Left(2)) scala> x.filter(_.isLeft).map(p) res0: Seq[Int] = List(0, 1) \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent Although \lstinline!x.filter(_.isLeft)! has type \lstinline!Seq[Either[Int, String]]!, +Although \lstinline!x.filter(_.isLeft)! has type \lstinline!Seq[Either[Int, String]]!, all values in that sequence are guaranteed to be of subtype \lstinline!Left!. So, we know it is safe to apply the partial function \lstinline!p! in \lstinline!map(p)!. @@ -205,18 +184,11 @@ \subsection{Partial functions of types and values} the safety of all operations at run time. For the example shown above, the \lstinline!collect! method would make a partial function, such as \lstinline!p!, safe to use: - -\begin{wrapfigure}{l}{0.45\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} scala> x.collect { case Left(y) => y - 1 } res1: Seq[Int] = List(0, 1) \end{lstlisting} - -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -\noindent The \lstinline!collect! method is a \index{total function}\textbf{total} +The \lstinline!collect! method is a \index{total function}\textbf{total} function because it is defined for all values of its arguments and does not throw exceptions. @@ -251,57 +223,35 @@ \subsection{Creating a partial type-to-value function (PTVF)} One example of a type constraint is shown by unfunctors\index{unfunctor} (see Section~\ref{subsec:Examples-of-non-functors}), which are type constructors whose type parameters are restricted to specific types. -As an example of an unfunctor, consider the code shown - -\begin{wrapfigure}{l}{0.54\columnwidth}% -\vspace{-0.6\baselineskip} +As an example of an unfunctor, consider this code: \begin{lstlisting} sealed trait Frac[A] // Unfunctor. final case class FracBD() extends Frac[BigDecimal] final case class FracD() extends Frac[Double] \end{lstlisting} - -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -\noindent at left, where values of type \lstinline!Frac[A]! can be -created only if \lstinline!A = BigDecimal! or \lstinline!A = Double!. -The keywords \lstinline!sealed! and \lstinline!final! guarantee -that no further code could extend this definition and allow us to -create a value of type, say, \lstinline!Frac[String]! or \lstinline!Frac[Boolean]!. +Values of type \lstinline!Frac[A]! can be created only if \lstinline!A = BigDecimal! +or \lstinline!A = Double!. The keywords \lstinline!sealed! and \lstinline!final! +guarantee that no further code could extend this definition and allow +us to create a value of type, say, \lstinline!Frac[String]! or \lstinline!Frac[Boolean]!. The Scala compiler will not detect any errors in the following code: - -\begin{wrapfigure}{l}{0.32\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} type T = Frac[String] type U = Frac[Boolean] \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent But we will never be able to create and use any values of -types \lstinline!T! or \lstinline!U!. In other words, the types -\lstinline!Frac[String]! and \lstinline!Frac[Boolean]! are \emph{void}\index{void type} -types. Trying to create and use values of these types will result -in type errors, as the following code shows: - -\begin{wrapfigure}{l}{0.46\columnwidth}% -\vspace{-0.95\baselineskip} +But we will never be able to create and use any values of types \lstinline!T! +or \lstinline!U!. In other words, the types \lstinline!Frac[String]! +and \lstinline!Frac[Boolean]! are \emph{void}\index{void type} types. +Trying to create and use values of these types will result in type +errors, as the following code shows: \begin{lstlisting}[numbers=left] def f[A]: Frac[A] = FracD() // Type error. val x: U = FracD() // Type error. val y: U = FracD().asInstanceOf[U] y match { case FracD() => } // Type error. \end{lstlisting} - -\vspace{-1.05\baselineskip} -\end{wrapfigure}% - -\noindent In line~3, we disabled the type checker and forced the -Scala compiler to ignore the type error in the definition of \lstinline!y!. -However, line~4 shows that we are unable to use that value \lstinline!y! +In line~3, we disabled the type checker and forced the Scala compiler +to ignore the type error in the definition of \lstinline!y!. However, +line~4 shows that we are unable to use that value \lstinline!y! in further computations. The type \lstinline!Frac[A]! is non-void (i.e., has values) only @@ -403,9 +353,6 @@ \subsection{Creating a partial type-to-value function (PTVF)} An equivalent implementation of the \lstinline!Frac! typeclass via a \lstinline!trait! with methods requires this code: - -\begin{wrapfigure}{l}{0.595\columnwidth}% -\vspace{-0.65\baselineskip} \begin{lstlisting} trait Frac[T] { // The trait is not `sealed`. def add(x: T, y: T): T @@ -420,12 +367,8 @@ \subsection{Creating a partial type-to-value function (PTVF)} def intdiv(x: Double, n: Int): Double = x / n } \end{lstlisting} - -\vspace{-0.85\baselineskip} -\end{wrapfigure}% - -\noindent The function \lstinline!avg[T]! works unchanged with this -implementation of \lstinline!Frac!. +The function \lstinline!avg[T]! works unchanged with this implementation +of \lstinline!Frac!. The \lstinline!trait!-based code is significantly longer than the code based on a case class. One advantage of the longer code is the @@ -698,7 +641,7 @@ \subsection{Extension methods} have to look at the library\textsf{'}s source code to determine the full name of the helper class that needs to be imported. -\subsection{Solved examples: Implementing typeclasses in practice\index{solved examples}} +\subsection{Examples: Implementing typeclasses in practice\index{examples (with code)}} We will now look at some practical examples of programming tasks implemented via typeclasses. @@ -795,9 +738,6 @@ \subsubsection{Example \label{subsec:tc-Example-metadata-extractors}\ref{subsec: } \end{lstlisting} With this definition, we can write: - -\begin{wrapfigure}{l}{0.34\columnwidth}% -\vspace{-0.85\baselineskip} \begin{lstlisting} scala> Data2("abc", 123, 0).name res3: String = "abc" @@ -805,18 +745,14 @@ \subsubsection{Example \label{subsec:tc-Example-metadata-extractors}\ref{subsec: scala> Data3(10, 20, "x").count res4: Long = 200 \end{lstlisting} - -\vspace{-0.95\baselineskip} -\end{wrapfigure}% - -\noindent The code looks as if the new methods \lstinline!name! and -\lstinline!count! became available for each of the supported data -types. It is important that the new methods were added \emph{without} -any changes to the implementations of \lstinline!Data1!, \lstinline!Data2!, -or \lstinline!Data3!. Those implementations may reside in external -libraries whose code we do not need to modify. The typeclass pattern -enables us to to add externally defined types to a type domain whenever -necessary, and to implement new PTVFs for them. +The code looks as if the new methods \lstinline!name! and \lstinline!count! +became available for each of the supported data types. It is important +that the new methods were added \emph{without} any changes to the +implementations of \lstinline!Data1!, \lstinline!Data2!, or \lstinline!Data3!. +Those implementations may reside in external libraries whose code +we do not need to modify. The typeclass pattern enables us to to add +externally defined types to a type domain whenever necessary, and +to implement new PTVFs for them. \subsubsection{Example \label{subsec:tc-Example-observability}\ref{subsec:tc-Example-observability} (counters)} @@ -1269,40 +1205,26 @@ \subsection{Typeclasses for type constructors\label{subsec:Typeclasses-for-type- As an example of a function parameterized by a type constructor, consider the following function \lstinline!inject! with a type parameter \lstinline!F! constrained to be a functor: - -\begin{wrapfigure}{l}{0.65\columnwidth}% -\vspace{-0.05\baselineskip} \begin{lstlisting} def inject[F[_]: Functor, A, B](a: A, f: F[B]): F[(A, B)] = ??? \end{lstlisting} -\vspace{-0.85\baselineskip} -\end{wrapfigure}% - -\noindent The \lstinline!Functor! typeclass implementing this constraint -will use the syntax \lstinline!Functor[F[_]]! because the type parameter +The \lstinline!Functor! typeclass implementing this constraint will +use the syntax \lstinline!Functor[F[_]]! because the type parameter \lstinline!F! is itself a type constructor. What information needs to be wrapped by a typeclass instance? A functor \lstinline!F! must have a \lstinline!map! function with the standard type signature: - -\begin{wrapfigure}{l}{0.43\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} def map[A, B](fa: F[A])(f: A => B): F[B] \end{lstlisting} -\vspace{-0.05\baselineskip} -\end{wrapfigure}% - -\noindent In the type notation, this type signature is written as:\vspace{-0.45\baselineskip} +In the type notation, this type signature is written as: \[ \text{map}:\forall(A,B).\,F^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow F^{B}\quad. \] -\vspace{-1.5\baselineskip} - -\noindent So, a typeclass instance of the \lstinline!Functor! typeclass -must contain this function as a value. But defining the typeclass -as before via a \lstinline!case class! does not work with Scala 2: +So, a typeclass instance of the \lstinline!Functor! typeclass must +contain this function as a value. But defining the typeclass as before +via a \lstinline!case class! does not work with Scala 2: \begin{lstlisting}[mathescape=true] final case class Functor[F[_]](map: $\forall(A,B).\,$F[A] => (A => B) => F[B]) // Not possible in Scala 2. \end{lstlisting} @@ -1310,21 +1232,15 @@ \subsection{Typeclasses for type constructors\label{subsec:Typeclasses-for-type- contains type quantifiers such as $\forall(A,B)$. In Scala 2, we need to represent such \textsf{``}nested\textsf{''} type quantifiers by writing a \lstinline!trait! with a \lstinline!def! method:\index{typeclass!Functor@\texttt{Functor}} - -\begin{wrapfigure}{l}{0.43\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] } \end{lstlisting} -\vspace{-0.75\baselineskip} -\end{wrapfigure}% - -\noindent The type constructor \lstinline!Functor! has the type parameter -\lstinline!F[_]!, which must be itself a type constructor. For any -type constructor \lstinline!F!, a value of type \lstinline!Functor[F]! -is a wrapper for a value of type $\forall(A,B).\,F^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow F^{B}$. +The type constructor \lstinline!Functor! has the type parameter \lstinline!F[_]!, +which must be itself a type constructor. For any type constructor +\lstinline!F!, a value of type \lstinline!Functor[F]! is a wrapper +for a value of type $\forall(A,B).\,F^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow F^{B}$. Values of type \lstinline!Functor! (i.e., typeclass instances) are implemented with the \textsf{``}\lstinline!new { ... }!\textsf{''} syntax: \begin{lstlisting} @@ -2685,12 +2601,8 @@ \subsection{Monoids\label{subsec:Monoids-constructions}} \lstinline!monoidS! remains the same; we need to rewrite \lstinline!monoidPair!, \lstinline!monoidEitherPreferB!, and \lstinline!monoidFunc! to accommodate the new definition of the \lstinline!Monoid! typeclass. The full -code is shown in Figure~\ref{fig:Implementation-of-Monoid-typeclass-with-trait}. - -The monoid laws hold for the recursive instances by induction (see -Section~\ref{subsec:The-Eq-typeclass}). +code is: -\begin{figure} \begin{lstlisting}[frame=single,fillcolor={\color{black}},framesep={0.2mm},framexleftmargin=2mm,framexrightmargin=2mm,framextopmargin=2mm,framexbottommargin=2mm] trait Monoid[T] { def empty: T @@ -2753,10 +2665,8 @@ \subsection{Monoids\label{subsec:Monoids-constructions}} res0: T = T(Right((abcabc,))) \end{lstlisting} -\caption{Implementing a recursive instance of the \lstinline!Monoid! typeclass -via a \lstinline!trait!. \label{fig:Implementation-of-Monoid-typeclass-with-trait}} -\end{figure} - +The monoid laws hold for the recursive instances by induction (see +Section~\ref{subsec:The-Eq-typeclass}). \paragraph{Summary} @@ -2823,17 +2733,11 @@ \subsection{Pointed functors: motivation and laws\label{subsec:Pointed-functors- type $F^{T}$ out of a single given value of type $T$. This operation, usually called \lstinline!pure! in Scala libraries, is implemented as a function with a type signature: - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} def pure[A]: A => F[A] \end{lstlisting} -\vspace{-0.8\baselineskip} -\end{wrapfigure}% - -\noindent The code notation for this function is $\text{pu}_{F}$, -and the type signature is written as $\text{pu}_{F}:\forall A.\,A\rightarrow F^{A}$. +The code notation for this function is $\text{pu}_{F}$, and the type +signature is written as $\text{pu}_{F}:\forall A.\,A\rightarrow F^{A}$. Some examples of pointed functors in Scala are \lstinline!Option!, \lstinline!List!, \lstinline!Try!, and \lstinline!Future!. Each @@ -2864,9 +2768,6 @@ \subsection{Pointed functors: motivation and laws\label{subsec:Pointed-functors- implicit val pointedTry = new PointedF[Try] { def pure[A]: A => Try[A] = x => Success(x) } \end{lstlisting} The PTVF \lstinline!pure! can be defined and used like this: - -\begin{wrapfigure}{l}{0.41\columnwidth}% -\vspace{-0.95\baselineskip} \begin{lstlisting} def pure[F[_]: PointedF, A](x: A): F[A] = implicitly[PointedF[F]].pure(x) @@ -2874,29 +2775,20 @@ \subsection{Pointed functors: motivation and laws\label{subsec:Pointed-functors- scala> pure[Option, Int](123) res0: Option[Int] = Some(123) \end{lstlisting} -\vspace{-0.95\baselineskip} -\end{wrapfigure}% - -\noindent When a pointed type constructor $F$ is a functor, we may -use both the functor\textsf{'}s \lstinline!map! method and the \lstinline!pure! -method. Do these two methods need to be compatible in some way? If -we \textsf{``}wrap\textsf{''} a value $123$ in a \lstinline!List! and then apply -\lstinline!.map(x => x + 1)!, we expect to obtain a list containing -$124$; any other result would break our intuition about \textsf{``}wrapping\textsf{''}. -We can generalize this situation to an arbitrary value $x^{:A}$ wrapped -using \lstinline!pure! and a function $f^{:A\rightarrow B}$ applied -to the wrapped value via \lstinline!map!: - -\begin{wrapfigure}{l}{0.41\columnwidth}% -\vspace{-0.8\baselineskip} +When a pointed type constructor $F$ is a functor, we may use both +the functor\textsf{'}s \lstinline!map! method and the \lstinline!pure! method. +Do these two methods need to be compatible in some way? If we \textsf{``}wrap\textsf{''} +a value $123$ in a \lstinline!List! and then apply \lstinline!.map(x => x + 1)!, +we expect to obtain a list containing $124$; any other result would +break our intuition about \textsf{``}wrapping\textsf{''}. We can generalize this situation +to an arbitrary value $x^{:A}$ wrapped using \lstinline!pure! and +a function $f^{:A\rightarrow B}$ applied to the wrapped value via +\lstinline!map!: \begin{lstlisting}[mathescape=true] pure(x).map(f) // $\color{dkgreen}\text{pu}_{F}(x)\triangleright\, \scriptstyle{f^{\uparrow F}}$ \end{lstlisting} -\vspace{-0.8\baselineskip} -\end{wrapfigure}% - -\noindent We expect that the result should be the same as a wrapped -$f(x)$. This expectation can be formulated as a law, called the \textbf{naturality +We expect that the result should be the same as a wrapped $f(x)$. +This expectation can be formulated as a law, called the \textbf{naturality law}\index{naturality law!of pure@of \texttt{pure}} of \lstinline!pure!; it must hold for any $f^{:A\rightarrow B}$: @@ -2919,20 +2811,12 @@ \subsection{Pointed functors: motivation and laws\label{subsec:Pointed-functors- \begin{equation} \text{pu}_{F}\bef f^{\uparrow F}=f\bef\text{pu}_{F}\quad.\label{eq:naturality-law-of-pure} \end{equation} - -\noindent \begin{wrapfigure}{l}{0.28\columnwidth}% -\vspace{-4.2\baselineskip} \[ \xymatrix{\xyScaleY{2.0pc}\xyScaleX{4.0pc}A\ar[r]\sp(0.5){\text{pu}_{F}}\ar[d]\sb(0.45){f} & F^{A}\ar[d]\sp(0.45){f^{\uparrow F}}\\ B\ar[r]\sp(0.5){\text{pu}_{F}} & F^{B} } \] -\vspace{-0.1\baselineskip} -\end{wrapfigure}% - -\noindent \vspace{-1.9\baselineskip} - -\noindent This motivates the following definition: A functor $F^{\bullet}$ +This motivates the following definition: A functor $F^{\bullet}$ is \index{pointed functor|textit}\textbf{pointed} if there exists a fully parametric function $\text{pu}_{F}:\forall A.\,A\rightarrow F^{A}$ satisfying the naturality law~(\ref{eq:naturality-law-of-pure}) @@ -2979,16 +2863,10 @@ \subsection{Pointed functors: motivation and laws\label{subsec:Pointed-functors- \] So, given just a \textsf{``}wrapped unit\textsf{''} value (denoted $\text{wu}_{F}$) of type $F^{\bbnum 1}$, we can define a new function $\text{pu}_{F}$: - -\begin{wrapfigure}{l}{0.46\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} def pure[A](x: A): F[A] = wu.map { _ => x } \end{lstlisting} -\vspace{-0.5\baselineskip} -\end{wrapfigure}% - -\noindent \vspace{-1\baselineskip} +In code notation: \begin{equation} \text{pu}_{F}^{:A\rightarrow F^{A}}\triangleq x^{:A}\rightarrow\text{wu}_{F}\triangleright(\_\rightarrow x)^{\uparrow F}\quad.\label{eq:pu-via-wu-def} \end{equation} @@ -3274,21 +3152,15 @@ \subsection{Pointed functors: structural analysis\label{subsec:Pointed-functors: \] In Scala, this is \lstinline!Left(())!. So, a \lstinline!Pointed! typeclass instance for $F$ is implemented by the following code: - -\begin{wrapfigure}{l}{0.38\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} type S[A, R] = Either[A, (A, R)] final case class F[A](s: S[A, F[A]]) implicit val pointedF: Pointed[F] = Pointed( F(Left(())) ) \end{lstlisting} -\vspace{-0.5\baselineskip} -\end{wrapfigure}% -\noindent The corresponding \lstinline!pure! method will use $F$\textsf{'}s -\lstinline!map! to transform $\text{wu}_{F}=1+\bbnum 0+\bbnum 0+...$ -into: +The corresponding \lstinline!pure! method will use $F$\textsf{'}s \lstinline!map! +to transform $\text{wu}_{F}=1+\bbnum 0+\bbnum 0+...$ into: \[ a+\bbnum 0+\bbnum 0+...:A+A\times A+A\times A\times A+...\quad. \] @@ -3396,26 +3268,20 @@ \subsection{Co-pointed functors\label{subsec:Co-pointed-functors}} \begin{equation} \text{ex}_{F}\bef f=f^{\uparrow F}\bef\text{ex}_{F}\quad.\label{eq:naturality-law-of-extract} \end{equation} - -\begin{wrapfigure}{l}{0.25\columnwidth}% -\vspace{-4.2\baselineskip} \[ \xymatrix{\xyScaleY{2.0pc}\xyScaleX{4.0pc}F^{A}\ar[r]\sp(0.5){\text{ex}_{F}}\ar[d]\sb(0.45){f^{\uparrow F}} & A\ar[d]\sp(0.45){~f}\\ F^{B}\ar[r]\sp(0.5){\text{ex}_{F}} & B } \] -\vspace{-1.5\baselineskip} -\end{wrapfigure}% - -\noindent The naturality law formulates our expectation that the extractor -function somehow \textsf{``}selects\textsf{''} a value of type $A$ among all the -values wrapped by $F^{A}$, and the \textsf{``}selection\textsf{''} works independently -of the values. If all wrapped values are transformed by a function -$f$ into wrapped values of type $B$, the extractor function will -still select a value of type $B$ in the same way as it did for values -of type $A$. So, the result will be the same as if we first extracted -a value of type $A$ and then transformed that value with $f$. +The naturality law formulates our expectation that the extractor function +somehow \textsf{``}selects\textsf{''} a value of type $A$ among all the values wrapped +by $F^{A}$, and the \textsf{``}selection\textsf{''} works independently of the values. +If all wrapped values are transformed by a function $f$ into wrapped +values of type $B$, the extractor function will still select a value +of type $B$ in the same way as it did for values of type $A$. So, +the result will be the same as if we first extracted a value of type +$A$ and then transformed that value with $f$. Both sides of the law~(\ref{eq:naturality-law-of-extract}) are functions of type $F^{A}\rightarrow B$. We saw in the previous section that, @@ -3541,11 +3407,11 @@ \subsection{Co-pointed functors\label{subsec:Co-pointed-functors}} implementation of the type signature $\text{ex}_{F+G}:F^{A}+G^{A}\rightarrow A$, given that we have functions $\text{ex}_{F}$ and $\text{ex}_{G}$: -\begin{wrapfigure}{l}{0.62\columnwidth}% +\begin{wrapfigure}{l}{0.56\columnwidth}% \vspace{-0.85\baselineskip} \begin{lstlisting} def copointedEitherFG[F[_]: Copointed, G[_]: Copointed]: - Copointed[Lambda[X => Either[F[X],G[X]]]] = + Copointed[Lambda[X => Either[F[X],G[X]]]] = new Copointed[Lambda[X => Either[F[X],G[X]]]] { def ex[A]: Either[F[A], G[A]] => A = { case Left(f) => extract(f) @@ -3696,8 +3562,9 @@ \subsection{Co-pointed functors\label{subsec:Co-pointed-functors}} \vspace{-2.25\baselineskip} \end{wrapfigure}% -\noindent In the code notation, the function $\text{ex}_{S}$ is written -as:\vspace{-0.2\baselineskip} +~ + +\noindent \vspace{-0.2\baselineskip} \[ \text{ex}_{S}\triangleq\,\begin{array}{|c||c|} & A\\ @@ -3984,7 +3851,7 @@ \section{Summary} We will discuss how to combine typeclasses in Section~\ref{subsec:Inheritance-and-automatic-typeclass} below. -\subsection{Solved examples\index{solved examples}} +\subsection{Examples\index{examples (with code)}} \subsubsection{Example \label{subsec:tc-Example-1}\ref{subsec:tc-Example-1}} @@ -4438,7 +4305,7 @@ \subsubsection{Example \label{subsec:tc-Example-10}\ref{subsec:tc-Example-10}{*} The required type signature is then implemented via a \lstinline!match! expression like this: -\begin{wrapfigure}{l}{0.58\columnwidth}% +\begin{wrapfigure}{l}{0.55\columnwidth}% \vspace{-0.75\baselineskip} \begin{lstlisting} def f[C[_]: Contrafunctor, A, B] @@ -4451,7 +4318,7 @@ \subsubsection{Example \label{subsec:tc-Example-10}\ref{subsec:tc-Example-10}{*} \vspace{-3\baselineskip} \end{wrapfigure}% -\noindent The code notation for this function is:\vspace{-0.25\baselineskip} +\noindent In the code notation:\vspace{-0.25\baselineskip} \[ f^{:C^{A}+C^{B}\rightarrow C^{A\times B}}\triangleq\,\begin{array}{|c||c|} & C^{A\times B}\\ @@ -4802,19 +4669,13 @@ \subsubsection{Exercise \label{subsec:tc-Exercise-9-1-1}\ref{subsec:tc-Exercise- \subsubsection{Exercise \label{subsec:tc-Exercise-9-1-1-1}\ref{subsec:tc-Exercise-9-1-1-1}{*}} (dual O\textsf{'}Connor\index{Russell O\textsf{'}Connor}) Assume that a functor $F$ -admits a function $q$ with type signature - -\begin{wrapfigure}{l}{0.52\columnwidth}% -\vspace{-0.75\baselineskip} +admits a function $q$ with type signature: \begin{lstlisting} def q[A, B, F[_]: Functor]: F[(A, B)] => (A, F[B]) \end{lstlisting} - -\vspace{-1.6\baselineskip} -\end{wrapfigure}% - -\noindent $q^{A,B}:F^{A\times B}\rightarrow A\times F^{B}$, additionally -satisfying the special laws of identity and associativity: +or, in code notation, $q^{A,B}:F^{A\times B}\rightarrow A\times F^{B}$. +Additionally, assume that $q$ satisfies the following special laws +of identity and associativity: \[ q^{\bbnum 1,B}=f^{:F^{\bbnum 1\times B}}\rightarrow1\times(f\triangleright(1\times b^{:B}\rightarrow b)^{\uparrow F})\quad,\quad\quad q^{A,B\times C}\bef(\text{id}^{A}\boxtimes q^{B,C})=q^{A\times B,C}\quad. \] @@ -4900,21 +4761,14 @@ \subsection{The existence of values for recursive types\label{subsec:Recursive-t all types $T$ satisfy that equation trivially, we find that the identity functor does not define any specific type $T$. If we translate this type equation into Scala code, we will run into a problem with recursion: - -\begin{wrapfigure}{l}{0.46\columnwidth}% -\vspace{-1\baselineskip} \begin{lstlisting} type Id[A] = A final case class T(s: Id[T]) // Equivalent to `final case class T(s: T)`. val x = T(x) // Infinite loop! \end{lstlisting} - -\vspace{-1.15\baselineskip} -\end{wrapfigure}% - -\noindent A value of \lstinline!case class T! can be created only -if we supply a value of type \lstinline!T!. Writing \lstinline!def x = T(x)! +A value of \lstinline!case class T! can be created only if we supply +a value of type \lstinline!T!. Writing \lstinline!def x = T(x)! instead of \lstinline!val! does not help: the evaluation of the code for \lstinline!x! will not terminate. We conclude that this recursive definition is invalid: one cannot create values of the resulting type @@ -4922,38 +4776,24 @@ \subsection{The existence of values for recursive types\label{subsec:Recursive-t Next, consider a product functor such as $S^{A}\triangleq A\times A\times\text{Int}$. Can we create values of type $T\triangleq S^{T}$? - -\begin{wrapfigure}{l}{0.46\columnwidth}% -\vspace{-0.8\baselineskip} \begin{lstlisting} final case class T(s: (T, T, Int)) val x = T(s = (x, x, 123)) // Infinite loop! \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent We again have an infinite loop when creating values of type -\lstinline!T!. As in the example with the identity functor, the case -class \lstinline!T! requires us to compute some values of type \lstinline!T! -before we can create a value of type \lstinline!T!. That requirement -is impossible to satisfy. +We again have an infinite loop when creating values of type \lstinline!T!. +As in the example with the identity functor, the case class \lstinline!T! +requires us to compute some values of type \lstinline!T! before we +can create a value of type \lstinline!T!. That requirement is impossible +to satisfy. For some disjunctive type constructors $S^{\bullet}$, values of type $\text{Fix}^{S}$ are created with no difficulty. One example is $S^{A}\triangleq\text{Int}+A\times A$: - -\begin{wrapfigure}{l}{0.46\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} final case class T(s: Either[Int, (T, T)]) val x: T = T(Left(123)) // OK val y: T = T(Right(x, x)) // OK \end{lstlisting} - -\vspace{-1\baselineskip} -\end{wrapfigure}% - -\noindent We are able to create \lstinline!x! of type $S^{\bbnum 0}\triangleq\text{Int}+\bbnum 0^{:T\times T}$ +We are able to create \lstinline!x! of type $S^{\bbnum 0}\triangleq\text{Int}+\bbnum 0^{:T\times T}$ without need for any previous values of $T$. We can then use \lstinline!x! to create a value \lstinline!y! of type $T$. This resembles defining a value by induction: the base case is the type $\text{Int}+\bbnum 0^{:T\times T}$, @@ -5209,25 +5049,15 @@ \subsubsection{Statement \label{subsec:Statement-array-list-equivalence}\ref{sub already defined for arrays of length $n$. So, we can compute $f_{1}(g'):\text{List}^{A}$ and thus create a value of type $\bbnum 0+A\times\text{List}^{A}$, which is equivalent to a value of type $\text{List}^{A}$. - -\begin{wrapfigure}{l}{0.4\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} def f1[A](arr: Array[A]): List[A] = if (arr.length == 0) List() else arr(0) :: f1(arr.tail) \end{lstlisting} - -\vspace{-1.25\baselineskip} -\end{wrapfigure}% - -\noindent \vspace{-1.25\baselineskip} \begin{align*} f_{1}(\text{Array}_{0}^{A}) & \triangleq\bbnum 1+\bbnum 0^{:A\times\text{List}^{A}}\quad,\\ f_{1}(g^{:\text{Int}_{[0,n]}\rightarrow A}) & \triangleq\bbnum 0+g(0)^{:A}\times\overline{f_{1}}(i\rightarrow g(i+1))\quad. \end{align*} -\vspace{-0.5\baselineskip} - To implement $f_{2}$, we use induction in the structure of the list. The length $n$ of the array is not known in advance and needs to be computed as we perform pattern matching on the given \lstinline!List[A]! @@ -5242,20 +5072,12 @@ \subsubsection{Statement \label{subsec:Statement-array-list-equivalence}\ref{sub So we define $f_{2}(\bbnum 0+x\times s)$ as a new array whose $0^{\text{th}}$ element is $x$ and the $i^{\text{th}}$ element is computed by applying the function $g\triangleq f_{2}(s)$ to $i-1$: - -\begin{wrapfigure}{l}{0.4363\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} def f2[A: ClassTag]: List[A] => Array[A] = { case List() => Array() case x :: s => Array(x) ++ f2.apply(s) } \end{lstlisting} - -\vspace{-1.25\baselineskip} -\end{wrapfigure}% - -\noindent \vspace{-1.25\baselineskip} \begin{align*} f_{2}(\bbnum 1+\bbnum 0^{:A\times\text{List}^{A}}) & \triangleq\text{Array}_{0}^{A}\quad,\\ f_{2}(\bbnum 0+x^{:A}\times s^{:\text{List}^{A}}) & \triangleq i^{:\text{Int}_{[0,n]}}\rightarrow\begin{cases} @@ -5263,7 +5085,6 @@ \subsubsection{Statement \label{subsec:Statement-array-list-equivalence}\ref{sub i\geq1: & \overline{f_{2}}(s)(i-1)\quad. \end{cases} \end{align*} -\vspace{-0.5\baselineskip} To show that $f_{1}$ and $f_{2}$ are inverse functions for each other, we again need to use induction. The base case is the empty @@ -5306,9 +5127,6 @@ \subsubsection{Statement \label{subsec:Statement-concat-array-as-list}\ref{subse The \lstinline!concat! function for lists is defined recursively as: - -\begin{wrapfigure}{l}{0.38\columnwidth}% -\vspace{-0.75\baselineskip} \begin{lstlisting} def concat[A](p: List[A], q: List[A]) : List[A] = p match { @@ -5316,11 +5134,6 @@ \subsubsection{Statement \label{subsec:Statement-concat-array-as-list}\ref{subse case a :: t => a :: concat(t, q) } \end{lstlisting} - -\vspace{-2.5\baselineskip} -\end{wrapfigure}% - -\noindent \vspace{-0.9\baselineskip} \[ p^{:\text{List}^{A}}\pplus q^{:\text{List}^{A}}\triangleq p\triangleright\begin{array}{|c||c|} & \text{List}^{A}\\ @@ -5604,9 +5417,7 @@ \subsection{Typeclasses with several type parameters (type relations)\label{subs single chosen unit (e.g., all lengths to kilometers and all masses to kilograms). Then the \lstinline!Convertible! relation will be many-to-one\index{type relation!many-to-one} instead of many-to-many. -The resulting code is shown in Figure~\ref{fig:Full-code-implementing-units-length-mass}. - -\begin{figure} +The resulting code is: \begin{lstlisting}[frame=single,fillcolor={\color{black}},framesep={0.2mm},framexleftmargin=2mm,framexrightmargin=2mm,framextopmargin=2mm,framexbottommargin=2mm] trait KG; trait LB; trait OZ; trait KM; trait MI; trait FT // Some supported units. @@ -5663,9 +5474,6 @@ \subsection{Typeclasses with several type parameters (type relations)\label{subs res3: Quantity[FT] = Quantity(4.0) \end{lstlisting} -\caption{Implementing type-safe computations with units of length and mass.\label{fig:Full-code-implementing-units-length-mass}} -\end{figure} - \subsection{Inheritance and automatic conversions of typeclasses\label{subsec:Inheritance-and-automatic-typeclass}} @@ -5712,22 +5520,15 @@ \subsection{Inheritance and automatic conversions of typeclasses\label{subsec:In Creating an instance of \lstinline!Monoid! for a type \lstinline!T! no longer requires having an instance of \lstinline!Semigroup! for \lstinline!T!: - -\begin{wrapfigure}{l}{0.48\columnwidth}% -\vspace{-0.95\baselineskip} \begin{lstlisting} implicit val c: Monoid[Int] = new Monoid[Int] { def empty: Int = 0 def combine: (Int, Int) => Int = _ + _ } \end{lstlisting} - -\vspace{-1.1\baselineskip} -\end{wrapfigure}% - -\noindent With this approach, we cannot avoid repeating the code for -\lstinline!def combine...! even if we \emph{do} already have a \lstinline!Semigroup! -instance. However, conversion from \lstinline!Monoid[T]! to \lstinline!Semigroup[T]! +With this approach, we cannot avoid repeating the code for \lstinline!def combine...! +even if we \emph{do} already have a \lstinline!Semigroup! instance. +However, conversion from \lstinline!Monoid[T]! to \lstinline!Semigroup[T]! is now automatic due to \textbf{object-oriented inheritance}\index{object-oriented inheritance}: Scala considers \lstinline!Monoid[T]! a subtype of \lstinline!Semigroup[T]! because the \lstinline!Monoid! class is declared as \lstinline!extends Semigroup!. @@ -5786,24 +5587,24 @@ \subsection{$P$-typeclasses, their laws and structure\label{subsec:P-algebraic-t \begin{table} \begin{centering} -\begin{tabular}{|c|c|c|c|} +\begin{tabular}{|c|c|c|} \hline -\textbf{\small{}Typeclass} & \textbf{\small{}Type of instance values} & \textbf{\small{}Inductive form} & \textbf{\small{}Structure functor}\tabularnewline +\textbf{\small{}Typeclass} & \textbf{\small{}Type of instance values} & \textbf{\small{}Inductive form}\tabularnewline \hline \hline -{\small{}pointed type} & {\small{}$\bbnum 1\rightarrow A$} & {\small{}$P^{A}\rightarrow A$} & {\small{}$P^{A}\triangleq\bbnum 1$}\tabularnewline +{\small{}pointed type} & {\small{}$\bbnum 1\rightarrow A$} & {\small{}$P^{A}\rightarrow A$}\tabularnewline \hline -{\small{}semigroup} & {\small{}$A\times A\rightarrow A$} & {\small{}$P^{A}\rightarrow A$} & {\small{}$P^{A}\triangleq A\times A$}\tabularnewline +{\small{}semigroup} & {\small{}$A\times A\rightarrow A$} & {\small{}$P^{A}\rightarrow A$}\tabularnewline \hline -{\small{}monoid} & {\small{}$\bbnum 1+A\times A\rightarrow A$} & {\small{}$P^{A}\rightarrow A$} & {\small{}$P^{A}\triangleq\bbnum 1+A\times A$}\tabularnewline +{\small{}monoid} & {\small{}$\bbnum 1+A\times A\rightarrow A$} & {\small{}$P^{A}\rightarrow A$}\tabularnewline \hline -{\small{}functor} & {\small{}$F^{A}\times\left(A\rightarrow B\right)\rightarrow F^{B}$} & {\small{}$\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$} & {\small{}$P^{A,B,F}\triangleq F^{A}\times\left(A\rightarrow B\right)$}\tabularnewline +{\small{}functor} & {\small{}$F^{A}\times\left(A\rightarrow B\right)\rightarrow F^{B}$} & {\small{}$\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$}\tabularnewline \hline -{\small{}pointed functor} & {\small{}$B+F^{A}\times\left(A\rightarrow B\right)\rightarrow F^{B}$} & {\small{}$\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$} & {\small{}$P^{A,B,F}\triangleq B+F^{A}\times\left(A\rightarrow B\right)$}\tabularnewline +{\small{}pointed functor} & {\small{}$B+F^{A}\times\left(A\rightarrow B\right)\rightarrow F^{B}$} & {\small{}$\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$}\tabularnewline \hline -{\small{}contrafunctor} & {\small{}$F^{A}\times(B\rightarrow A)\rightarrow F^{B}$} & {\small{}$\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$} & {\small{}$P^{A,B,F}\triangleq F^{A}\times\left(B\rightarrow A\right)$}\tabularnewline +{\small{}contrafunctor} & {\small{}$F^{A}\times(B\rightarrow A)\rightarrow F^{B}$} & {\small{}$\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$}\tabularnewline \hline -{\small{}pointed contrafunctor} & {\small{}$\bbnum 1+F^{A}\times\left(B\rightarrow A\right)\rightarrow F^{B}$} & {\small{}$\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$} & {\small{}$P^{A,B,F}\triangleq\bbnum 1+F^{A}\times\left(B\rightarrow A\right)$}\tabularnewline +{\small{}pointed contrafunctor} & {\small{}$\bbnum 1+F^{A}\times\left(B\rightarrow A\right)\rightarrow F^{B}$} & {\small{}$\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$}\tabularnewline \hline \end{tabular} \par\end{centering} @@ -5852,8 +5653,10 @@ \subsection{$P$-typeclasses, their laws and structure\label{subsec:P-algebraic-t evidence values are expressed as functions with several type parameters, such as $\forall(A,B).\,P^{A,B,F}\rightarrow F^{B}$. This type signature is of the form $P^{F}\rightarrow F$ except for the additional type -parameters. To simplify the presentation in this section, we will -only consider typeclasses for simple types, such as \lstinline!Semigroup! +parameters. The laws of type constructor typeclasses are also more +complicated because they often involve multiple type parameters and +arbitrary functions. To simplify the presentation in this section, +we will only consider typeclasses for simple types, such as \lstinline!Semigroup! or \lstinline!Monoid!. Implementing the \lstinline!Monoid! typeclass via a structure functor diff --git a/sofp-src/sofp.lyx b/sofp-src/sofp.lyx index d2496405e..37775e7a2 100644 --- a/sofp-src/sofp.lyx +++ b/sofp-src/sofp.lyx @@ -193,7 +193,7 @@ \renewcommand{\ogreaterthan}{\boxrightarrow} \renewcommand{\varogreaterthan}{\boxrightarrow} \end_preamble -\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt +\options open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt \use_default_options true \maintain_unincluded_children false \language english @@ -262,12 +262,12 @@ \end_index \paperwidth 7.444in \paperheight 9.68in -\leftmargin 2.2cm -\topmargin 1.175cm -\rightmargin 1.3cm -\bottommargin 1.275cm -\headsep 0.4cm -\footskip 0.72cm +\leftmargin 2.4cm +\topmargin 1.3cm +\rightmargin 1.4cm +\bottommargin 1.5cm +\headsep 0.5cm +\footskip 0.8cm \secnumdepth 3 \tocdepth 2 \paragraph_separation indent @@ -370,10 +370,12 @@ literal "false" \series default - in 2022 + in 2024 \end_layout \begin_layout Uppertitleback + +\size footnotesize Copyright \begin_inset ERT status open @@ -389,7 +391,7 @@ copyright \end_inset - 2018-2022 by Sergei Winitzki + 2018-2024 by Sergei Winitzki \begin_inset Newline newline \end_inset @@ -401,42 +403,38 @@ copyright \begin_inset Newline newline \end_inset -Printed copies may be ordered at -\family typewriter \begin_inset CommandInset href LatexCommand href -target "http://www.lulu.com/content/paperback-book/24915714" +name "Print on-demand link at lulu.com" +target "https://www.lulu.com/en/us/shop/sergei-winitzki/the-science-of-functional-programming-draft-version/paperback/product-1y5zzgje.html" literal "false" \end_inset -\family default - -\begin_inset Note Note -status collapsed - -\begin_layout Plain Layout -https://www.lulu.com/en/us/shop/sergei-winitzki/the-science-of-functional-programm -ing-draft-version/paperback/product-1y5zzgje.html -\end_layout - +\begin_inset Newline newline \end_inset -\begin_inset Newline newline +\begin_inset space ~ \end_inset -\begin_inset space ~ +\begin_inset Newline newline \end_inset +ISBN (e-book): 978-0-359-76877-6 +\size default \begin_inset Newline newline \end_inset + +\size footnotesize ISBN: 978-0-359-76877-6 +\size default + \begin_inset Newline newline \end_inset @@ -538,14 +536,6 @@ Assets \begin_inset Quotes erd \end_inset - as -\family typewriter -sofp.pdf -\family default - or -\family typewriter -sofp-draft.pdf -\family default . The source code may be also included as a \begin_inset Quotes eld @@ -560,7 +550,7 @@ file attachment sofp-src.tar.bz2 \family default within a PDF file. - To extract, run the command + To extract, run \family typewriter `pdftk sofp.pdf unpack_files output .` \family default @@ -573,12 +563,12 @@ sofp-src.tar.bz2 \family typewriter README.md \family default - for compilation instructions. + for build instructions. \end_layout \begin_layout Lowertitleback -\size small +\size scriptsize This book is a pedagogical in-depth tutorial and reference on the theory of functional programming (FP) as practiced in the early 21 \begin_inset Formula $^{\text{st}}$ @@ -599,7 +589,7 @@ This book is a pedagogical in-depth tutorial and reference on the theory The book's topics include working with FP-style collections; reasoning about recursive functions and types; the Curry-Howard correspondence; laws, structura -l analysis, and code for functors, monads, and other typeclasses built upon +l analysis, and code for functors, monads, and other typeclasses based on exponential-polynomial data types; techniques of symbolic derivation and proof; free typeclass constructions; and parametricity theorems. \begin_inset Newline newline @@ -612,7 +602,7 @@ l analysis, and code for functors, monads, and other typeclasses built upon Long and difficult, yet boring explanations are logically developed in excruciat ing detail through NUMBEROFCODESNIPPETS Scala code snippets, NUMBEROFSTATEMENTS statements with step-by-step derivations, NUMBEROFDIAGRAMS diagrams, NUMBEROFEX -AMPLES solved examples with tested Scala code, and NUMBEROFEXERCISES exercises. +AMPLES examples with tested Scala code, and NUMBEROFEXERCISES exercises. Discussions build upon each chapter's material further. \begin_inset Newline newline \end_inset @@ -621,70 +611,22 @@ AMPLES solved examples with tested Scala code, and NUMBEROFEXERCISES exercises. \begin_inset Newline newline \end_inset -Beginners in FP will find tutorials about the -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -map -\end_layout - -\end_inset - -/ -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -reduce -\end_layout - -\end_inset - - programming style, type parameters, disjunctive types, and higher-order - functions. +Beginners in FP will find tutorials about the map/reduce programming style, + type parameters, disjunctive types, and higher-order functions. For more advanced readers, the book shows the practical uses of the Curry-Howa rd correspondence and the parametricity theorems without unnecessary jargon; proves that all the standard monads (e.g., -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - +\family typewriter List -\end_layout - -\end_inset - +\family default or -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - +\family typewriter State -\end_layout - -\end_inset - +\family default ) satisfy the monad laws; derives lawful instances of -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - +\family typewriter Functor -\end_layout - -\end_inset - +\family default and other typeclasses from types; shows that monad transformers need 18 laws; and explains the use of parametricity for reasoning about the Church encoding and the free typeclasses. @@ -698,8 +640,8 @@ Functor Readers should have a working knowledge of programming; e.g., be able to write code that prints the number of distinct words in a sentence. The difficulty of this book's mathematical derivations is at the level - of undergraduate calculus, similar to that of multiplying matrices or simplifyi -ng the expressions: + of undergraduate multivariate calculus, similar to that of multiplying + matrices or simplifying the expressions: \begin_inset Formula \[ \frac{1}{x-2}-\frac{1}{x+2}\quad\text{ and }\quad\frac{d}{dx}\left((x+1)f(x)e^{-x}\right)\quad. @@ -711,12 +653,16 @@ ng the expressions: \begin_inset Newline newline \end_inset -Sergei Winitzki received a Ph.D. +The author received a Ph.D. \begin_inset space ~ \end_inset in theoretical physics. - After a career in academic research, he currently works as a software engineer. + After a career in academic research, he works as a software engineer. +\begin_inset Newline newline +\end_inset + + \end_layout \begin_layout Standard @@ -790,7 +736,7 @@ pagenumbering{arabic} \begin_layout Standard \begin_inset Note Comment -status open +status collapsed \begin_layout Plain Layout + Each chapter is now a separate file. @@ -857,7 +803,7 @@ filename "sofp-preface.lyx" \end_layout \begin_layout Part -Beginner level +Introductory level \end_layout \begin_layout Standard @@ -882,13 +828,6 @@ filename "sofp-disjunctions.lyx" \end_inset -\end_layout - -\begin_layout Part -Intermediate level -\end_layout - -\begin_layout Standard \begin_inset CommandInset include LatexCommand include filename "sofp-higher-order-functions.lyx" @@ -903,6 +842,17 @@ filename "sofp-curry-howard.lyx" \end_inset +\begin_inset CommandInset include +LatexCommand include +filename "sofp-essay1.lyx" + +\end_inset + + +\end_layout + +\begin_layout Part +Intermediate level \end_layout \begin_layout Standard @@ -955,6 +905,13 @@ filename "sofp-traversable.lyx" \end_inset +\begin_inset CommandInset include +LatexCommand include +filename "sofp-essay2.lyx" + +\end_inset + + \end_layout \begin_layout Part @@ -978,7 +935,7 @@ filename "sofp-transformers.lyx" \end_layout -\begin_layout Part +\begin_layout Addpart Discussions \end_layout @@ -990,19 +947,16 @@ filename "sofp-summary.lyx" \end_inset -\end_layout - -\begin_layout Standard \begin_inset CommandInset include LatexCommand include -filename "sofp-essays.lyx" +filename "sofp-essay3.lyx" \end_inset \end_layout -\begin_layout Part +\begin_layout Addpart Appendixes \end_layout diff --git a/sofp-src/sofp.tex b/sofp-src/sofp.tex index 500e37f87..0487fe885 100644 --- a/sofp-src/sofp.tex +++ b/sofp-src/sofp.tex @@ -1,6 +1,6 @@ -%% LyX 2.3.6.2 created this file. For more info, see http://www.lyx.org/. +%% LyX 2.3.7 created this file. For more info, see http://www.lyx.org/. %% Do not edit unless you really know what you are doing. -\documentclass[10pt,russian,english,open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=10pt]{scrbook} +\documentclass[10pt,russian,english,open=any,numbers=noenddot,index=totoc,bibliography=totoc,listof=totoc,fontsize=12pt]{scrbook} \usepackage{amsmath} \usepackage{mathpazo} \usepackage{helvet} @@ -9,7 +9,7 @@ \usepackage[T2A,T1]{fontenc} \usepackage[utf8]{inputenc} \usepackage[paperwidth=7.444in,paperheight=9.68in]{geometry} -\geometry{verbose,tmargin=1.175cm,bmargin=1.275cm,lmargin=2.2cm,rmargin=1.3cm,headsep=0.4cm,footskip=0.72cm} +\geometry{verbose,tmargin=1.3cm,bmargin=1.5cm,lmargin=2.4cm,rmargin=1.4cm,headsep=0.5cm,footskip=0.8cm} \setcounter{secnumdepth}{3} \usepackage{xcolor} \usepackage{babel} @@ -312,16 +312,17 @@ \subtitle{A tutorial, with examples in Scala} \author{by Sergei Winitzki, Ph.D.} \date{Draft version of \today} -\publishers{Published by\textbf{ \href{http://lulu.com}{lulu.com}} in 2022} -\uppertitleback{Copyright \copyright\ 2018-2022 by Sergei Winitzki\\ -~\\ -Printed copies may be ordered at \texttt{\href{http://www.lulu.com/content/paperback-book/24915714}{http://www.lulu.com/content/paperback-book/24915714}}\\ -~\\ -ISBN: 978-0-359-76877-6\\ +\publishers{Published by\textbf{ \href{http://lulu.com}{lulu.com}} in 2024} +\uppertitleback{{\footnotesize{}Copyright \copyright\ 2018-2024 by Sergei Winitzki}\\ +{\footnotesize{}~}\\ +{\footnotesize{}\href{https://www.lulu.com/en/us/shop/sergei-winitzki/the-science-of-functional-programming-draft-version/paperback/product-1y5zzgje.html}{Print on-demand link at lulu.com}}\\ +{\footnotesize{}~}\\ +{\footnotesize{}ISBN (e-book): 978-0-359-76877-6}\\ +{\footnotesize{}ISBN: 978-0-359-76877-6}\\ \\ -{\scriptsize{}Source hash (sha256): 7021afb267710bda7cc2e88a7b546e72bf1aea86986f6378ac3e5675290c3d33}\\ -{\scriptsize{}Git commit: 5d4080136db424d3adbde2e03951b349b1335f50}\\ -{\scriptsize{}PDF file built by pdfTeX 3.14159265-2.6-1.40.20 (TeX Live 2019) on Tue, 29 Aug 2023 22:38:56 +0200 by Darwin 22.6.0}\\ +{\scriptsize{}Source hash (sha256): 3c45ab56c2c3650f8698f220383b662ee6e806c37e65cd59b239e959895d50e5}\\ +{\scriptsize{}Git commit: 63d39f9ed35dda029870f71207f741464e2c87cf}\\ +{\scriptsize{}PDF file built by pdfTeX 3.141592653-2.6-1.40.25 (TeX Live 2023) on Mon, 22 Jan 2024 20:38:49 +0100 by Darwin 22.6.0}\\ ~\\ {\scriptsize{}Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, @@ -334,59 +335,57 @@ of the source code for the book is available at }\texttt{\scriptsize{}\href{https://github.com/winitzki/sofp}{https://github.com/winitzki/sofp}}{\scriptsize{} and includes LyX, LaTeX, graphics source files, and build scripts. A full-color hyperlinked PDF file is available at }\texttt{\scriptsize{}\href{https://github.com/winitzki/sofp/releases}{https://github.com/winitzki/sofp/releases}}{\scriptsize{} -under \textsf{``}Assets\textsf{''} as }\texttt{\scriptsize{}sofp.pdf}{\scriptsize{} -or }\texttt{\scriptsize{}sofp-draft.pdf}{\scriptsize{}. The source -code may be also included as a \textsf{``}file attachment\textsf{''} named }\texttt{\scriptsize{}sofp-src.tar.bz2}{\scriptsize{} -within a PDF file. To extract, run the command }\texttt{\scriptsize{}`pdftk -sofp.pdf unpack\_files output .`}{\scriptsize{} and then }\texttt{\scriptsize{}`tar +under \textsf{``}Assets\textsf{''}. The source code may be also included as a \textsf{``}file +attachment\textsf{''} named }\texttt{\scriptsize{}sofp-src.tar.bz2}{\scriptsize{} +within a PDF file. To extract, run }\texttt{\scriptsize{}`pdftk sofp.pdf +unpack\_files output .`}{\scriptsize{} and then }\texttt{\scriptsize{}`tar jxvf sofp-src.tar.bz2`}{\scriptsize{}. See the file }\texttt{\scriptsize{}README.md}{\scriptsize{} -for compilation instructions.}} -\lowertitleback{{\small{}This book is a pedagogical in-depth tutorial and reference +for build instructions.}} +\lowertitleback{{\scriptsize{}This book is a pedagogical in-depth tutorial and reference on the theory of functional programming (FP) as practiced in the early 21$^{\text{st}}$ century. Starting from issues found in practical coding, the book builds up the theoretical intuition, knowledge, and techniques that programmers need for rigorous reasoning about types and code. Examples are given in Scala, but most of the material applies equally to other FP languages.}\\ -{\small{}}\\ -{\small{}The book\textsf{'}s topics include working with FP-style collections; +{\scriptsize{}}\\ +{\scriptsize{}The book\textsf{'}s topics include working with FP-style collections; reasoning about recursive functions and types; the Curry-Howard correspondence; laws, structural analysis, and code for functors, monads, and other -typeclasses built upon exponential-polynomial data types; techniques +typeclasses based on exponential-polynomial data types; techniques of symbolic derivation and proof; free typeclass constructions; and parametricity theorems.}\\ -{\small{}}\\ -{\small{}Long and difficult, yet boring explanations are logically -developed in excruciating detail through 1860 Scala -code snippets, 187 statements with step-by-step derivations, -103 diagrams, 216 solved examples with tested -Scala code, and 291 exercises. Discussions build upon -each chapter\textsf{'}s material further.}\\ -{\small{}}\\ -{\small{}Beginners in FP will find tutorials about the }\inputencoding{latin9}\lstinline!map!\inputencoding{utf8}{\small{}/}\inputencoding{latin9}\lstinline!reduce!\inputencoding{utf8}{\small{} +{\scriptsize{}}\\ +{\scriptsize{}Long and difficult, yet boring explanations are logically +developed in excruciating detail through 1871 Scala +code snippets, 189 statements with step-by-step derivations, +103 diagrams, 216 examples with tested Scala +code, and 295 exercises. Discussions build upon each +chapter\textsf{'}s material further.}\\ +{\scriptsize{}}\\ +{\scriptsize{}Beginners in FP will find tutorials about the map/reduce programming style, type parameters, disjunctive types, and higher-order functions. For more advanced readers, the book shows the practical uses of the Curry-Howard correspondence and the parametricity theorems without unnecessary jargon; proves that all the standard monads (e.g., -}\inputencoding{latin9}\lstinline!List!\inputencoding{utf8}{\small{} -or }\inputencoding{latin9}\lstinline!State!\inputencoding{utf8}{\small{}) -satisfy the monad laws; derives lawful instances of }\inputencoding{latin9}\lstinline!Functor!\inputencoding{utf8}{\small{} +}\texttt{\scriptsize{}List}{\scriptsize{} or }\texttt{\scriptsize{}State}{\scriptsize{}) +satisfy the monad laws; derives lawful instances of }\texttt{\scriptsize{}Functor}{\scriptsize{} and other typeclasses from types; shows that monad transformers need 18 laws; and explains the use of parametricity for reasoning about the Church encoding and the free typeclasses.}\\ -{\small{}}\\ -{\small{}Readers should have a working knowledge of programming; e.g., -be able to write code that prints the number of distinct words in -a sentence. The difficulty of this book\textsf{'}s mathematical derivations -is at the level of undergraduate calculus, similar to that of multiplying -matrices or simplifying the expressions: +{\scriptsize{}}\\ +{\scriptsize{}Readers should have a working knowledge of programming; +e.g., be able to write code that prints the number of distinct words +in a sentence. The difficulty of this book\textsf{'}s mathematical derivations +is at the level of undergraduate multivariate calculus, similar to +that of multiplying matrices or simplifying the expressions: \[ \frac{1}{x-2}-\frac{1}{x+2}\quad\text{ and }\quad\frac{d}{dx}\left((x+1)f(x)e^{-x}\right)\quad. \] }\\ -{\small{}Sergei Winitzki received a Ph.D.~in theoretical physics. -After a career in academic research, he currently works as a software -engineer.}} +{\scriptsize{}The author received a Ph.D.~in theoretical physics. +After a career in academic research, he works as a software engineer.}\\ +} \maketitle \frontmatter\pagenumbering{roman} @@ -424,27 +423,23 @@ \end{comment} \include{sofp-preface} -\part{Beginner level} +\part{Introductory level} -\include{sofp-nameless-functions}\include{sofp-induction}\include{sofp-disjunctions} +\include{sofp-nameless-functions}\include{sofp-induction}\include{sofp-disjunctions}\include{sofp-higher-order-functions}\include{sofp-curry-howard}\include{sofp-essay1} \part{Intermediate level} -\include{sofp-higher-order-functions}\include{sofp-curry-howard} - -\include{sofp-functors}\include{sofp-reasoning}\include{sofp-typeclasses}\include{sofp-filterable}\include{sofp-monads}\include{sofp-applicative}\include{sofp-traversable} +\include{sofp-functors}\include{sofp-reasoning}\include{sofp-typeclasses}\include{sofp-filterable}\include{sofp-monads}\include{sofp-applicative}\include{sofp-traversable}\include{sofp-essay2} \part{Advanced level} \include{sofp-free-type}\include{sofp-transformers} -\part{Discussions} - -\include{sofp-summary} +\addpart{Discussions} -\include{sofp-essays} +\include{sofp-summary}\include{sofp-essay3} -\part{Appendixes} +\addpart{Appendixes} \appendix