parent
844dc1de63
commit
ca87271792
@ -0,0 +1,18 @@
|
||||
.DS_Store
|
||||
thesis/_minted-thesis
|
||||
thesis/thesis.aux
|
||||
thesis/thesis.bbl
|
||||
thesis/thesis.bcf
|
||||
thesis/thesis.blg
|
||||
thesis/thesis.fdb_latexmk
|
||||
thesis/thesis.fls
|
||||
thesis/thesis.lof
|
||||
thesis/thesis.log
|
||||
thesis/thesis.lot
|
||||
thesis/thesis.out
|
||||
thesis/thesis.tex.d
|
||||
thesis/thesis.toc
|
||||
thesis/thesis.ist
|
||||
thesis/code.*
|
||||
*.swp
|
||||
*.bak
|
@ -0,0 +1,28 @@
|
||||
[submodule "work/common-list-functions/shellcheck"]
|
||||
path = work/common-list-functions/shellcheck
|
||||
url = https://github.com/koalaman/shellcheck.git
|
||||
[submodule "work/common-list-functions/pandoc"]
|
||||
path = work/common-list-functions/pandoc
|
||||
url = https://github.com/jgm/pandoc.git
|
||||
[submodule "work/common-list-functions/postgrest"]
|
||||
path = work/common-list-functions/postgrest
|
||||
url = https://github.com/PostgREST/postgrest.git
|
||||
[submodule "work/common-list-functions/semantic"]
|
||||
path = work/common-list-functions/semantic
|
||||
url = https://github.com/github/semantic.git
|
||||
[submodule "work/common-list-functions/purescript"]
|
||||
path = work/common-list-functions/purescript
|
||||
url = https://github.com/purescript/purescript.git
|
||||
[submodule "work/common-list-functions/compiler"]
|
||||
path = work/common-list-functions/compiler
|
||||
url = https://github.com/elm/compiler.git
|
||||
[submodule "work/common-list-functions/Haxl"]
|
||||
path = work/common-list-functions/Haxl
|
||||
url = https://github.com/facebook/Haxl.git
|
||||
[submodule "work/go"]
|
||||
path = work/go
|
||||
url = https://github.com/tommyknows/go.git
|
||||
branch = bachelor-thesis
|
||||
[submodule "work/funcheck"]
|
||||
path = work/funcheck
|
||||
url = https://github.com/tommyknows/funcheck.git
|
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -0,0 +1,23 @@
|
||||
# Functional Go
|
||||
|
||||
<!--- `Build this document with docker run -it -v (pwd):/build/ pandoc/latex -s /build/README.md -o /build/README.pdf` -->
|
||||
|
||||
See `thesis.pdf` for the actual thesis document. That itself links to `thesis/thesis.pdf`.
|
||||
|
||||
Structure and Files:
|
||||
|
||||
- `thesis`: contains the latex code plus a pre-compiled version of the document.
|
||||
There are also further tools in that directory that are needed to build the
|
||||
document.
|
||||
- `work`: contains subfolders for practical work that was done.
|
||||
- `common-list-functions`: See the thesis' appendix on what it is.
|
||||
Contains git submodules and a shell script to acquire the results
|
||||
that are described in the thesis.
|
||||
- `examples`: contains different examples of functional Go code. Some code snippets
|
||||
are described in the thesis, some have been developed out of curiosity.
|
||||
- `funcheck`: git submodule that contains the implementation of `funcheck' as described
|
||||
in the thesis.
|
||||
- `go`: git submodule with the Go compiler code.
|
||||
- `proposals`: contains old proposals that where submitted.
|
||||
|
||||
See Appendix 1 in `thesis.pdf` for more information.
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,35 @@
|
||||
# Functional Go
|
||||
|
||||
## Situation
|
||||
|
||||
With the rise of Javascript, Rust, and Go, the functional programming paradigm has
|
||||
gained popularity too. Though none of these programming languages are purely functional,
|
||||
they all share the common feature of having the possibility to use functional concepts.
|
||||
However, a lot of programmers struggle initially with the concept of functional
|
||||
programming. Learning a purely functional programming language is extremely useful
|
||||
to gain familiarity with these concepts. Purely functional programming languages
|
||||
like Haskell though are not known for their beginner-friendliness.
|
||||
What makes learning a functional language difficult is that not only does the
|
||||
programmer have to learn an entirely different paradigm, but also a syntax that
|
||||
is uncommon for people coming from imperative or object-oriented languages.
|
||||
|
||||
## Objective
|
||||
|
||||
The objective is to ease the entry into functional programming by providing a
|
||||
"harness" for Go that enforces a purely functional style. This harness can either
|
||||
be a separate stand-alone tool (like `gofmt` or `gopls`) or built into the Go
|
||||
compiler directly.
|
||||
The functional Go code could still be valid Go code, but the harness could also apply
|
||||
simple transformations to the code to make it more performant.
|
||||
Things to consider while implementing that harness would be immutability (without
|
||||
introducing unnecessary complications), the possibility to make functional Go code
|
||||
run and / or compile with regular Go code (code translation?).
|
||||
Another possibility would be to implement something like "functional-check". What
|
||||
`shellcheck` is to shell-scripts, `funccheck` is to Go code. It lints existing
|
||||
Go code and points out the bits that are not functional, displaying rules and
|
||||
examples. While being conceptually simpler, this would leave most options to the
|
||||
programmer.
|
||||
|
||||
In the end, functional Go should be syntactically familiar to people that have
|
||||
worked with Go (or C in that regard). With that, one can learn the concepts related
|
||||
to functional programming, without also needing to learn a new language and syntax.
|
Binary file not shown.
@ -0,0 +1 @@
|
||||
thesis/thesis.pdf
|
@ -0,0 +1,67 @@
|
||||
LATEXMK ?= latexmk
|
||||
PDFLATEX ?= pdflatex
|
||||
PLANTUML ?= plantuml
|
||||
MAIN_TEX ?= ./thesis.tex
|
||||
|
||||
MKARGS = -interaction=nonstopmode -use-make -shell-escape
|
||||
|
||||
default: pdf
|
||||
|
||||
dvi: $(MAIN_TEX:.tex=.dvi)
|
||||
pdf: $(MAIN_TEX:.tex=.pdf)
|
||||
|
||||
# Rules to compile zhawthesis document class
|
||||
zhawthesis.cls: zhawthesis.dtx
|
||||
'$(PDFLATEX)' zhawthesis.dtx
|
||||
|
||||
# Rule to compile glossary
|
||||
thesis.gls:
|
||||
makeglossaries thesis
|
||||
skins.sty:
|
||||
|
||||
|
||||
# Dependencies of thesis TeX
|
||||
-include $(MAIN_TEX).d
|
||||
|
||||
# Thesis document creation rules (DVI, PDF)
|
||||
$(MAIN_TEX:.tex=.dvi): $(MAIN_TEX)
|
||||
'$(LATEXMK)' -dvi -pdf- -ps- -deps-out='$<.d' $(MKARGS) '$<'
|
||||
|
||||
$(MAIN_TEX:.tex=.pdf): $(MAIN_TEX)
|
||||
'$(LATEXMK)' -pdf -dvi- -ps- -deps-out='$<.d' $(MKARGS) '$<'
|
||||
|
||||
# Continuous recompilation when the source files change
|
||||
watch:
|
||||
'$(LATEXMK)' -pvc -pdf -dvi- -ps- $(MKARGS) '$(MAIN_TEX)'
|
||||
|
||||
# Open the compiled pdf
|
||||
view: $(MAIN_TEX:.tex=.pdf)
|
||||
'$(shell command -v xdg-open open | head -n 1)' '$<' >/dev/null 2>&1 &
|
||||
|
||||
# Clean up temprary files
|
||||
clean:
|
||||
'$(LATEXMK)' -C '$(MAIN_TEX)'
|
||||
$(RM) zhawthesis.aux zhawthesis.glo zhawthesis.hd zhawthesis.idx zhawthesis.log zhawthesis.out
|
||||
$(RM) *.synctex.gz *.bbl *.ilg
|
||||
$(RM) '$(MAIN_TEX).d'
|
||||
$(RM) *.ist
|
||||
|
||||
.PHONY: clean dvi pdf view watch
|
||||
|
||||
|
||||
# Resource compilation rules
|
||||
%-%.pdf: %.mp
|
||||
cd '$(dir $(@))' && mpost '$(realpath $(@:.pdf=.mp))'
|
||||
|
||||
%.eps: %.plantuml
|
||||
$(PLANTUML) -teps -v '$<'
|
||||
|
||||
%.pdf: %.eps
|
||||
epstopdf -o='$@' '$<'
|
||||
|
||||
%.pdf: %.svg
|
||||
rsvg-convert -f pdf -o '$@' -d 300 -p 300 '$<'
|
||||
|
||||
|
||||
logos/zhaw/%.pdf: logos/zhaw/%.ai
|
||||
inkscape --without-gui --export-file='$@' --export-area-drawing --export-margin=1 '$<'
|
@ -0,0 +1,12 @@
|
||||
# zhawthesis
|
||||
|
||||
Structure follows: https://gpmpublic.zhaw.ch/GPMDocProdDPublic/2_Studium/2_05_Lehre_Studium/T_VL_Vorlage_Berichtstruktur_PA_BA.docx
|
||||
|
||||
## Requirements
|
||||
|
||||
- A (pdf)LaTeX installation (including the most commonly used packages)
|
||||
- BibTeX (for bibliography management)
|
||||
|
||||
"Optional":
|
||||
- GNU Make
|
||||
- `inkscape` (to convert `*.ai` ZHAW logos to PDF)
|
Binary file not shown.
@ -0,0 +1,42 @@
|
||||
% -*- mode: latex; coding: utf-8; TeX-master: ../thesis -*-
|
||||
% !TEX TS-program = pdflatexmk
|
||||
% !TEX encoding = UTF-8 Unicode
|
||||
% !TEX root = ../thesis.tex
|
||||
|
||||
In the last decade, concepts from functional programming have grown in
|
||||
importance within the wider, non-functional programming community.
|
||||
Often it is recommended to learn a purely functional programming language
|
||||
such as Haskell to become familiar with these concepts.
|
||||
However, many programmers struggle with the double duty
|
||||
of learning a new paradigm and a new syntax at the same time.
|
||||
This paper proposes that by learning functional programming with a
|
||||
multi-paradigm programming language and a familiar syntax it is possible
|
||||
to lower this effort.
|
||||
|
||||
To achieve this goal, the programming language Go has been chosen due to
|
||||
its syntactical simplicity and familiarity.
|
||||
However, a downside of Go is the lack of a built-in list type, as lists take a
|
||||
central role in functional programming. Although this is remediated by Go's slices,
|
||||
they are not accompanied by any higher-order list processing functions --- `map', `filter', and `fold' to name a
|
||||
few --- that are present in every functional programming language (and many
|
||||
other languages too).
|
||||
Due to the absence of polymorphism, in order to provide these higher-order functions in
|
||||
a user-friendly way it is necessary to build these functions into the compiler.
|
||||
|
||||
Furthermore, this paper adopts a definition of pure functional programming and
|
||||
introduces `funcheck', a static code analysis tool that is designed to
|
||||
report constructs that are non-functional.
|
||||
|
||||
In conclusion, I demonstrate that with the help of the newly built-in
|
||||
functions `fmap', `filter', `foldr', `foldl' and
|
||||
`prepend', as well as `funcheck' to lint code, Go proves itself to be a
|
||||
suitable language for getting started with functional programming.
|
||||
The primary factor for this is reflected in the Go idiom `clear is better than clever'.
|
||||
While functional Go code is more verbose when compared to functional languages, it
|
||||
is also more obvious about its inner workings.
|
||||
At the same time, it also illustrates why there is no way around learning a
|
||||
language such as Haskell if fluency with functional programming concepts
|
||||
is desired. The main reasons are that, although it may be unusual at first, Haskell's
|
||||
syntax is extremely concise, and that the language's design --- the type system,
|
||||
pattern matching, the purity guarantees and more --- provides a very effective toolset
|
||||
for purely functional programming.
|
@ -0,0 +1,36 @@
|
||||
% -*- mode: latex; coding: utf-8; TeX-master: ../thesis -*-
|
||||
% !TEX TS-program = pdflatexmk
|
||||
% !TEX encoding = UTF-8 Unicode
|
||||
% !TEX root = ../thesis.tex
|
||||
|
||||
Innerhalb der letzten zehn Jahre haben Konzepte und Ideen aus dem funktionalen
|
||||
Programmieren im Alltag von vielen Entwicklern Fuss gefasst. Häufig wird
|
||||
empfohlen, eine rein funktionale Programmiersprache wie zum Beispiel Haskell
|
||||
zu lernen, um sich mit diesen Konzepten vertraut zu machen. Viele haben jedoch
|
||||
Mühe, eine neue Syntax und ein neues Paradigma gleichzeitig zu lernen. Das Ziel
|
||||
dieser Arbeit ist deswegen, mit Hilfe einer multiparadigmatischen Programmiersprache mit
|
||||
bekannter Syntax einen einfacheren Einstieg in funktionales Programmieren zu ermöglichen.
|
||||
|
||||
Um dieses Ziel zu erreichen, wurde die Programmiersprache Go aufgrund ihrer
|
||||
syntaktischen Simplizität und Vertrautheit gewählt.
|
||||
Da Listen jedoch oft eine zentrale Rolle im funktionalen Programmieren einnehmen, ist ein
|
||||
Nachteil dieser Wahl, dass Go keinen eingebauten Datentyp für Listen besitzt. Zwar wird
|
||||
dieser Nachteil durch Go's `Slices' gemildert, jedoch fehlen viele Funktionen höherer
|
||||
Ordnung um mit Listen zu arbeiten --- `map', `filter' und `reduce', um einige zu nennen.
|
||||
Da Go's Typensystem keinen Polymorphismus bietet, müssen diese Funktionen im Compiler
|
||||
implementiert werden, um eine möglichst benutzerfreundliche Verwendung zu ermöglichen.
|
||||
|
||||
Zusätzlich dazu wird die Bedeutung von rein funktionalem Programmieren im Kontext dieser Arbeit
|
||||
festgelegt und auf Basis dieser Definition das Code-Analyse Tool `funcheck' entwickelt, welches
|
||||
nicht-funktionale Konstrukte im Programmcode meldet.
|
||||
|
||||
Mit den neuen eingebauten Funktionen `fmap', `filter', `foldr', `foldl' und `prepend',
|
||||
sowie dem Linter `funcheck' erweist sich Go als geeignete Programmiersprache um
|
||||
einen einfachen Einstieg in funktionales Programmieren zu ermöglichen. Der primäre Grund
|
||||
spiegelt sich auch im Go Idiom `clear is better than clever' wider. Obwohl funktionaler
|
||||
Go Code länger ist als in funktionalen Sprachen, ist dieser auch einfacher nachzuvollziehen.
|
||||
Des Weiteren zeigt die Arbeit aber auch, dass es keine Alternative zu einer rein funktionalen
|
||||
Sprache wie Haskell gibt, um sich funktionales Programmieren vollständig anzueignen.
|
||||
Haskell's zwar ungewöhnliche, aber prägnante Syntax sowie das Design
|
||||
der Sprache --- das Typensystem, Pattern Matching, die Reinheitsgarantien und vieles mehr ---
|
||||
bilden hierfür eine solide und oft verwendete Grundlage.
|
@ -0,0 +1,35 @@
|
||||
% -*- mode: latex; coding: utf-8; TeX-master: ../thesis -*-
|
||||
% !TEX TS-program = pdflatexmk
|
||||
% !TEX encoding = UTF-8 Unicode
|
||||
% !TEX root = ../thesis.tex
|
||||
|
||||
As a part of my bachelor studies, I chose to attend a course on functional programming.
|
||||
Having worked with Go for the last 3 years, first-class and higher-order functions
|
||||
were not particularly new ideas to me. However, learning Haskell was, at the beginning,
|
||||
overwhelming.
|
||||
|
||||
I decided to rewrite the exercises that I did not understand in Go.
|
||||
%So what I did for some exercises that I could not understand completely is that
|
||||
%I rewrote them in Go.
|
||||
The result was more verbose; usually
|
||||
roughly two to three times the lines of code for the same algorithm.
|
||||
However, after writing the Go version, I understood not only the Go version,
|
||||
but also the Haskell version.
|
||||
|
||||
After doing this several times, I realised that I was constantly rewriting
|
||||
the same higher-order functions with different types, but more or less the same
|
||||
implementation. Thus, the idea of adding them as built-ins came up.
|
||||
|
||||
`Funcheck' then came into play when I wanted to build something in Go first and
|
||||
later rewrite it in Haskell. It was hard to tell whether it was purely functional,
|
||||
but it needed to be in order to be easier to write the implementation in Haskell.
|
||||
|
||||
This thesis is written based on my own struggles I had with Haskell, and it is
|
||||
my hope that someday, someone may benefit from the work done in this thesis.
|
||||
|
||||
Special thanks to:
|
||||
|
||||
My supervisors Gerrit Burkert and Karl Rege for their support and guidance,
|
||||
Tom Whiston for proofreading this thesis and improving my English,
|
||||
Eva Kuske for the consultation on writing and
|
||||
my employer \href{http://nine.ch}{nine} for their flexible work hours.
|
@ -0,0 +1,246 @@
|
||||
% -*- mode: latex; coding: utf-8; TeX-master: ../thesis -*-
|
||||
% !TEX TS-program = pdflatexmk
|
||||
% !TEX encoding = UTF-8 Unicode
|
||||
% !TEX root = ../thesis.tex
|
||||
|
||||
\section{Learning Functional Programming}
|
||||
|
||||
Within the last decade, concepts from functional programming have been brought into the daily life
|
||||
of almost every programmer. There are many events that contributed to this gain in popularity:
|
||||
|
||||
In 2007, C\# 3.0 was released, which introduced lambda expressions and laid the foundations for
|
||||
turning C\# into a hybrid Object-Oriented / Functional language\autocite{csharp-functional}.
|
||||
Two years later, Ryan Dhal published the initial version of Node.js, eliminating JavaScript's
|
||||
ties to the browser and introducing it as a server-side programming language, increasing the
|
||||
adoption of JavaScript further.
|
||||
In 2013, Java 8 was released and brought support for lambda expressions and streams.
|
||||
Within the same time frame, Python has been rapidly growing in popularity\autocite{python-popularity}.
|
||||
|
||||
Further, many new multi-paradigm programming languages have been introduced,
|
||||
including Rust, Kotlin, Go and Dart. They all have functions as first-class citizens in
|
||||
the language since their initial release.
|
||||
|
||||
With these developments, it can be said that functional programming has emerged
|
||||
from niche use-cases and academia to truly arrive in the wider programming community.
|
||||
For example Rust, the `most popular programming language' for 5 years in a row (2016--2020)
|
||||
according to the Stack Overflow Developer survey\autocite{rust-loved}, has been significantly
|
||||
influenced by functional programming languages\autocite{rust-functional}. Further, in idiomatic
|
||||
Rust code, a functional style can be clearly observed\footnote{A simple example for this may be
|
||||
that variables are immutable by default.}.
|
||||
|
||||
Learning a purely functional programming language increases fluency with these concepts and
|
||||
teaches a different way to think and approach problems when programming. Due to this, many
|
||||
people recommend learning a functional programming
|
||||
language\autocite{blog1-funcprog}\autocite{blog2-funcprog}\autocite{blog3-funcprog}\autocite{blog4-funcprog},
|
||||
even if one may not end up using that language at all\autocite{quora-funcprog}.
|
||||
|
||||
Most literature about functional programming,
|
||||
including academia and online resources like blogs, contain code examples written in Haskell.
|
||||
Further, according to the Tiobe Index\autocite{tiobe-index}, Haskell is also the most popular
|
||||
purely functional programming language\autocite{comparison-functional-languages}.
|
||||
|
||||
\section{Haskell}
|
||||
|
||||
Haskell, the \textit{lingua franca} amongst functional programmers, is a lazily-evaluated, purely functional programming
|
||||
language. While Haskell's strengths stem from all it's features like its advanced type system, pattern matching and more,
|
||||
these features are also what makes Haskell famously hard to learn\autocite{haskell-hard-one}\autocite{haskell-hard-two}\autocite{haskell-hard-three}\autocite{haskell-hard-four}.
|
||||
|
||||
Beginner Haskell programmers face a very distinctive challenge in contrast to learning a new, non-functional programming language:
|
||||
Not only do they need to learn a new language with an unusual syntax (compared to imperative or object-oriented languages), they
|
||||
also need to change their way of thinking and reasoning about problems.
|
||||
For example, the renowned quicksort-implementation from the Haskell Introduction Page\autocite{haskell-quicksort}:
|
||||
|
||||
\begin{listing}
|
||||
\begin{haskellcode}
|
||||
quicksort :: Ord a => [a] -> [a]
|
||||
quicksort [] = []
|
||||
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
|
||||
where
|
||||
lesser = filter (< p) xs
|
||||
greater = filter (>= p) xs
|
||||
\end{haskellcode}
|
||||
\caption{Quicksort implementation in Haskell}\label{code:haskell-quicksort}
|
||||
\end{listing}
|
||||
|
||||
While this is only a very short and clean piece of code, these 6 lines already pose many challenges to non-experienced Haskellers:
|
||||
|
||||
\begin{itemize}
|
||||
\item The function's signature with no `fn' or `func' statement as they often appear in imperative languages
|
||||
\item The pattern matching, which would be a `switch' statement or a chain of `if / else' conditions
|
||||
\item The deconstruction of the list within the pattern matching
|
||||
\item The functional nature of the program, passing `(< p)' (a function returning a function) to another function
|
||||
\item The function call to `filter' without parenthesised arguments and no clear indicator at which arguments
|
||||
it takes and which types are returned
|
||||
\end{itemize}
|
||||
|
||||
Although some of these constructs also exist in imperative or object-oriented languages, the cumulative difference
|
||||
is not to underestimate and adds to Haskell's steep learning curve.
|
||||
|
||||
\section{Goals}
|
||||
|
||||
As demonstrated in the example above, learning a new paradigm and syntax at the same time
|
||||
can be daunting and discouraging for novices.
|
||||
The entry barrier for functional programming should be lowered by
|
||||
using a modern, multi-paradigm language with a clear and familiar syntax. The functional
|
||||
programming beginner should be able to focus on the paradigm first, and then change to a language
|
||||
like Haskell to fully get into functional programming.
|
||||
|
||||
To achieve this goal, this thesis will consist of two parts.
|
||||
In the first part, writing functional code will be made as easy as possible. This means that
|
||||
a programming language with an easy and familiar syntax should be chosen. Optimally, this language
|
||||
should already support functions as first-class citizens. Additionally, it should be statically
|
||||
typed, as a static type system makes it easier to reason about a program and can support the
|
||||
programmer while writing code.
|
||||
In the second part, a linter will be created to check code for non-functional statements. To achieve
|
||||
this, a definition of what functional purity means has to be selected and a ruleset has to be
|
||||
worked out and implemented into a static analysis tool.
|
||||
|
||||
\section{Why Go}\label{sec:why-go}
|
||||
|
||||
The language of choice for this task is Go, a statically typed, garbage-collected programming language
|
||||
designed at Google in 2009\autocite{golang-publish}. With its strong syntactic similarity to C, it should
|
||||
be familiar to most programmers.
|
||||
|
||||
Go strives for simplicity and its syntax is extremely small and easy to learn. For example, the
|
||||
language consists of only 25 keywords and purposefully omits constructs like the ternary operator
|
||||
(<bool> ? <then> : <else>) as a replacement for the longer `if <bool> \{ <then> \} else \{ <else> \}'
|
||||
for clarity. `A language needs only one conditional control flow construct'\autocite{go-ternary},
|
||||
and this also holds true for many other constructs. In Go, there is usually only one way
|
||||
to express something, improving the clarity of code.
|
||||
|
||||
Due to this clarity and unambiguity, the language is a perfect fit to grasp the concepts and trace
|
||||
the inner workings of functional programming. It should be easy to read code and understand what
|
||||
it does without a lot of experience with the language.
|
||||
|
||||
There are however a few downsides of using Go. Currently, Go does not have polymorphism, which means
|
||||
that functions always have to be written with specific types. Due to this, Go also does not include
|
||||
common list processing functions like `map', `filter', `reduce' and more\footnote{Although Go does
|
||||
have some polymorphic functions like `append', these are specified as built-in functions in the
|
||||
language and not user-defined}. Further, Go does not have a built-in `list' datatype. However, Go's
|
||||
`slices' cover a lot of use cases for lists already. Section~\ref{sec:go-slices} covers this topic
|
||||
in more detail.
|
||||
|
||||
\section{Existing Work}
|
||||
|
||||
With Go's support of some functional aspects, patterns and best practices have emerged that make
|
||||
us of functional programming constructs.
|
||||
For example, in the \textit{net/http} package of the standard library, the function
|
||||
\begin{gocode}
|
||||
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
|
||||
\end{gocode}
|
||||
is used to register functions for http server handling\autocite{go-http-doc}:
|
||||
|
||||
\begin{code}
|
||||
\begin{gocode}
|
||||
func myHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Handle the given HTTP request
|
||||
}
|
||||
|
||||
func main() {
|
||||
// register myHandler in the default ServeMux
|
||||
http.HandleFunc("/", myHandler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Go web server handler function}
|
||||
\end{code}
|
||||
|
||||
Using functions as function parameters or return types is a commonly used feature in Go, not just
|
||||
within the standard library. Furthermore, design patterns have emerged within the community
|
||||
that use functional concepts. An example of this are `functional options'.
|
||||
|
||||
\subsection{Functional Options}
|
||||
|
||||
The `functional options' pattern has been outlined in Dave Cheney's blog post `Functional options
|
||||
for friendly APIs'\autocite{functional-options} and is a great example on how to use the support for multiple paradigms.
|
||||
The basic idea with functional options is that a type constructor receives an unknown (0-n) amount
|
||||
of options:
|
||||
\begin{code}
|
||||
\begin{gocode}
|
||||
func New(requiredSetting string, opts ...option) *MyType {
|
||||
t := &MyType{
|
||||
setting: requiredSetting,
|
||||
featureX: false,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
type option func(t *MyType)
|
||||
\end{gocode}
|
||||
\caption{Constructor with functional options}
|
||||
\end{code}
|
||||
|
||||
These options can then access the instance of \mintinline{go}|MyType| to modify it accordingly,
|
||||
for example:
|
||||
|
||||
\begin{code}
|
||||
\begin{gocode}
|
||||
func EnableFeatureX() option {
|
||||
return func(t *MyType) {
|
||||
t.featureX = true
|
||||
}
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Example for a functional option}
|
||||
\end{code}
|
||||
|
||||
To enable feature X, `New' can be called with that option:
|
||||
\begin{gocode}
|
||||
t := New("required", EnableFeatureX())
|
||||
\end{gocode}
|
||||
|
||||
With this pattern, it is easy to introduce new options without breaking old usages of the API.
|
||||
Furthermore, the typical `config struct' pattern can be avoided and meaningful zero values
|
||||
can be set.
|
||||
|
||||
A more extensive example on how functional options are implemented and used can be found in
|
||||
Appendix~\ref{appendix:funcopts}.
|
||||
|
||||
\begin{quote}
|
||||
In summary
|
||||
\begin{itemize}
|
||||
\item Functional options let you write APIs that can grow over time.
|
||||
\item They enable the default use case to be the simplest.
|
||||
\item They provide meaningful configuration parameters.
|
||||
\item Finally they give you access to the entire power of the language to initialize complex values.
|
||||
\end{itemize}\autocite{functional-options}
|
||||
\end{quote}
|
||||
|
||||
While this is a great example of what can be done with support for functional concepts, a purely functional approach to
|
||||
Go has so far been discouraged by the core Go team, which is understandable for a multi-paradigm programming language.
|
||||
However, multiple developers have already researched and tested Go's ability to do functional programming.
|
||||
|
||||
\subsection{Functional Go?}
|
||||
|
||||
In his talk `Functional Go'\autocite{func-go-talk}, Francesc Campoy Flores analysed some commonly used functional
|
||||
language features in Haskell and how they can be copied to Go. Ignoring speed and stack overflows due to non-existent
|
||||
tail call optimisation\autocite{go-tco}, the main issue is with the type system and the missing polymorphism.
|
||||
|
||||
\subsection{go-functional}
|
||||
|
||||
In July 2017, Aaron Schlesinger, a Go programmer for Microsoft Azure, gave a talk on functional programming with Go.
|
||||
He released a repository\autocite{go-functional} that contains `core utilities for functional Programming in Go'.
|
||||
The project is currently unmaintained, but showcases functional programming concepts like currying, functors and
|
||||
monoids in Go. In the `README' file of the repository, he also states that:
|
||||
\begin{quote}
|
||||
Note that the types herein are hard-coded for specific types, but you could
|
||||
use code generation to produce these FP constructs for any type you please!
|
||||
\autocite{go-functional-readme}
|
||||
\end{quote}
|
||||
|
||||
\section{Verdict}
|
||||
|
||||
The aforementioned projects showcase the main issue with functional programming in Go: the missing
|
||||
helper functions that are prevalent in functional languages and that they currently cannot be implemented
|
||||
in a generic way.
|
||||
|
||||
To make functional programming more accessible in Go, this thesis will research what the most used
|
||||
higher-order functions are and implement them with a focus on usability.
|
||||
Furthermore, to learn purely functional programming, a list of rules for pure functional code should
|
||||
be curated and implemented in a static code analysis tool. This tool can then be used to check
|
||||
existing code and report constructs that are not functional.
|
@ -0,0 +1,273 @@
|
||||
This chapter introduces the core concepts in Go that are needed to follow this paper.
|
||||
Go is a language similar to C, although with a few minor, but important differences.
|
||||
First, Go is garbage collected, meaning that the programmer does not need to allocate
|
||||
and free memory\footnote{although allocating memory is possible with the \mintinline{go}|new|
|
||||
built-in function}. Secondly, Go does not allow for pointer arithmetic.
|
||||
`Without pointer arithmetic it's possible to create a language that can never derive an
|
||||
illegal address that succeeds incorrectly'\autocite{go-pointerarithmetic}.
|
||||
|
||||
Further, Go provides a built-in data type that does not exist in plain C: slices.
|
||||
|
||||
\section{Go Slices}\label{sec:go-slices}
|
||||
|
||||
As mentioned in Section~\ref{sec:why-go}, Go does not have a `list' implementation and lists are rarely used.
|
||||
The reason for this
|
||||
is twofold. Firstly, as Go does not have polymorphism, it is not possible for users to implement a generic
|
||||
`list' type that would work with any underlying type. Secondly, the Go authors added `slices' as a core type
|
||||
to the language. From a usage perspective, lists would not add anything compared to slices.
|
||||
|
||||
Go's Slices can be viewed as an abstraction over arrays, to mitigate some of the weaknesses of arrays
|
||||
when compared to lists.
|
||||
|
||||
\begin{quote}
|
||||
Arrays have their place, but they're a bit inflexible, so you don't see them too often in Go code.
|
||||
Slices, though, are everywhere. They build on arrays to provide great power and convenience.\autocite{golang-slices}
|
||||
\end{quote}
|
||||
|
||||
Slices can be visualised as a `struct' over an array:
|
||||
|
||||
\begin{gocode}
|
||||
// NOTE: this type does not really exist, it
|
||||
// is just to visualise how they are implemented.
|
||||
type Slice struct {
|
||||
// the underlying "backing store" array
|
||||
array *[]T
|
||||
// the length of the slice / view on the array
|
||||
len int
|
||||
// the capacity of the array from the
|
||||
// starting index of the slice
|
||||
cap int
|
||||
}
|
||||
\end{gocode}
|
||||
|
||||
With the `append' function, elements can be added to a slice. Should the underlying array not have enough
|
||||
capacity left to store the new elements, a new array will be created and the data from the old array will
|
||||
be copied into the new one. This happens transparently to the user.
|
||||
|
||||
\subsection{Using Slices}
|
||||
|
||||
`head', `tail' and `last' operations can be done with index expressions:
|
||||
|
||||
\begin{gocode}
|
||||
// []<T> initialises a slice, while [n]<T> initialises an
|
||||
// array, which is of fixed length n. We're only working
|
||||
// with slices here.
|
||||
s := []string{"first", "second", "third"}
|
||||
head := s[0]
|
||||
tail := s[1:]
|
||||
last := s[len(s)-1]
|
||||
\end{gocode}
|
||||
|
||||
Adding elements or joining slices is achieved with `append':
|
||||
|
||||
\begin{gocode}
|
||||
s := []string{"first", "second"}
|
||||
s = append(s, "third", "fourth")
|
||||
t := []string{"fifth", "seventh"}
|
||||
s = append(s, t...)
|
||||
// to prepend an element, one has to create a
|
||||
// slice out of that element
|
||||
s = append([]string{"zeroth"}, s...)
|
||||
\end{gocode}
|
||||
|
||||
Append is a variadic function, meaning it takes \textit{n} elements. If the slice is of type \mintinline{go}|[]T|,
|
||||
the appended elements have to be of type \mintinline{go}|T|.
|
||||
|
||||
To join two lists, the second list is expanded into
|
||||
variadic arguments.
|
||||
|
||||
More complex operations like removing elements, inserting elements in the middle or finding
|
||||
elements in a slice require helper functions, which have also been documented in Go's
|
||||
Slice Tricks\autocite{slice-tricks}.
|
||||
|
||||
\subsection{What is missing from Slices}
|
||||
|
||||
This quick glance at slices should clarify that, though the runtime characteristics of lists and slices
|
||||
can differ, from a usage standpoint, what is possible with lists is also possible with slices.
|
||||
|
||||
In a typical program written in a functional language, lists take a central role\footnote{Interestingly,
|
||||
there is no clear and direct answer as to why they do. One reason may be because they are recursively
|
||||
defined and trivially to implement functionally. Further, they are easier to use than arrays, where
|
||||
the programmer would need to track the index and bound of the array (imagine keeping track of the
|
||||
indices in a recursive function)\autocite{why-lists}}. Because of this, functional languages have
|
||||
a number of helper functions like `map', `filter' and `fold'\autocite{haskell-list-funcs} to modify and
|
||||
work on lists. These so called `higher order functions' currently do
|
||||
not exist in Go and would need to be implemented by the programmer. With no support for polymorphism, a
|
||||
different implementation would need to be written for every slice-type that is used. The type \mintinline{go}|[]int|
|
||||
(read: a slice of integers) differs from \mintinline{go}|[]string|, which means that a possible
|
||||
`map' function would have to be written once to support slices of integers, once to support slices
|
||||
of strings, and a combination of these two:
|
||||
|
||||
\begin{gocode}
|
||||
func mapIntToInt(f func(int) int, []int) []int
|
||||
func mapIntToString(f func(int) string, []int) []string
|
||||
func mapStringToInt(f func(string) int, []string) []int
|
||||
func mapStringToString(f func(string) string, []string) []string
|
||||
\end{gocode}
|
||||
|
||||
With 7 base types (eliding the different `int' types like `int8', `uint16`, `int16', etc.), this would
|
||||
mean $7^{2} = 49$ map functions just to cover the base types. Counting the different numeric
|
||||
types into that equation (totally 19 distinct types\autocite{go-basetypes}), would grow that number to $19^{2} = 361$ functions.
|
||||
|
||||
Though this code could be generated, it misses user-defined types which would still
|
||||
need to be generated separately in a pre-compile step.
|
||||
|
||||
Another option, instead of having a function per type, would be that `map' takes and returns empty interfaces
|
||||
(\mintinline{go}|interface{}|). However, `the empty interface says
|
||||
nothing'\autocite{empty-interface}. The declaration of `map' would be:
|
||||
\begin{gocode}
|
||||
func map(f func(interface{}) interface{}, interface{}) interface{}
|
||||
\end{gocode}
|
||||
|
||||
This function header does not say anything about it's types, which would
|
||||
mean that they would need to be checked at runtime and handled gracefully. It
|
||||
would also require the caller to do a type assertion after every call. Further,
|
||||
a slice of type \mintinline{go}|T| cannot simply be converted or asserted to a slice of another type
|
||||
\mintinline{go}|T2|\autocite{go-interface-slice-conv}\autocite{go-interface-slice-conv2}.
|
||||
Because of this limitation, a typical usage pattern of map would be:
|
||||
\begin{gocode}
|
||||
s := []string{"hello", "world"}
|
||||
var i []interface{}
|
||||
for _, e := range s {
|
||||
i = append(i, e)
|
||||
}
|
||||
// i is now populated and can be used.
|
||||
r := map(someFunc, f)
|
||||
// to convert it back to []string:
|
||||
s = r.([]string)
|
||||
\end{gocode}
|
||||
|
||||
This exemplifies why using the empty interface is not an option. Further, the
|
||||
function could not really be type-checked at compile time, as there is no indication
|
||||
of which argument's type must be equal.
|
||||
|
||||
\section{Built-in functions}
|
||||
|
||||
To mitigate these issues, the most common list-operations (in Go slice-operations) will
|
||||
be added as built-ins to the compiler, so that the programmer can use these functions
|
||||
on every slice-type without any conversion or code generation being necessary.
|
||||
|
||||
The language specification defines what built-in functions are and which built-in
|
||||
functions should exist:
|
||||
\begin{quote}
|
||||
Built-in functions are predeclared. They are called like any other function
|
||||
but some of them accept a type instead of an expression as the first argument.
|
||||
|
||||
The built-in functions do not have standard Go types, so they can only appear
|
||||
in call expressions; they cannot be used as function values.\autocite{go-spec-builtins}
|
||||
\end{quote}
|
||||
|
||||
For example, the documentation for the built-in \mintinline{go}|append|:
|
||||
\begin{gocode}
|
||||
// The append built-in function appends elements to the end of a slice.
|
||||
// ...
|
||||
func append(slice []Type, elems ...Type) []Type
|
||||
\end{gocode}
|
||||
|
||||
The documentation shows that the types supplied to append are not specified upfront.
|
||||
Instead, they are resolved and checked during compilation of the program.
|
||||
|
||||
Thus, in order to have generic list processing functions, these functions need to
|
||||
be implemented as built-ins in the compiler.
|
||||
|
||||
\section{The Go Compiler}
|
||||
|
||||
The Go programming language is defined by its specification\autocite{go-spec}, and not
|
||||
it's implementation. As of Go 1.14, there are two major implementations of that
|
||||
specification; Google's self-hosting compiler toolchain `gc', which is written in
|
||||
Go, and `gccgo', a front end for GCC, written in C++.
|
||||
|
||||
When talking about the Go compiler, what's mostly referred to is `gc'\footnote{`gc' stands
|
||||
for `go compiler', and not `garbage collection' (which is abbreviated as `GC').}.
|
||||
|
||||
A famous, although not completely correct story tells about Go being designed
|
||||
while a C++ program was compiling\autocite{less-is-more}.
|
||||
This is why one of the main goals when designing Go was fast compilation times:
|
||||
\begin{quote}
|
||||
Finally, working with Go is intended to be fast: it should take at most a few
|
||||
seconds to build a large executable on a single computer. To meet these goals
|
||||
required addressing a number of linguistic issues: an expressive but lightweight
|
||||
type system; concurrency and garbage collection; rigid dependency specification;
|
||||
and so on. These cannot be addressed well by libraries or tools; a new language
|
||||
was called for.\autocite{go-faq}
|
||||
\end{quote}
|
||||
|
||||
Go has taken some measures to combat slow compilation times. In general, Go's dependency resolution is simpler
|
||||
compared to other languages, for example by not allowing circular dependencies.
|
||||
Furthermore, compilation is not even attempted if there are unused
|
||||
imports or unused declarations of variables, types and functions.
|
||||
This leads to less code to compile and in turn shorter compilation times.
|
||||
Another reason is that `the `gc' compiler is simpler code compared to most
|
||||
recent compilers'\autocite{nuts-compiler}. However, according
|
||||
to Rob Pike, one of the creators of Go, Go's compiler is not notably fast, but
|
||||
most other compilers are slow:
|
||||
|
||||
\begin{quote}
|
||||
The compiler hasn't even been properly tuned for speed. A truly fast compiler
|
||||
would generate the same quality code much faster.\autocite{nuts-compiler}
|
||||
\end{quote}
|
||||
|
||||
The code generation with the `gc' compiler is split into four phases:
|
||||
|
||||
\subsection{Parsing Go programs}
|
||||
|
||||
\newglossaryentry{dag}{name=DAG, description={Directed Acyclic Graph}}
|
||||
\newglossaryentry{ast}{name=AST,description={Abstract Syntax Tree, an abstract representation of source code as a tree}}
|
||||
|
||||
The first phase of compilation is parsing Go programs into a syntax tree. This
|
||||
is done by tokenising the code (`lexical analysis' - the `lexer'), parsing
|
||||
(`syntax analysis' - the `parser') it and then constructing a syntax tree
|
||||
(\gls{ast})\footnote{Technically, the syntax tree is a syntax \gls{dag}\autocite{ast-node-dag}}
|
||||
for each source file.
|
||||
|
||||
Go has been designed to be easy to parse and analyse. For example, in contrast
|
||||
to many other programming languages,
|
||||
Go does not require a symbol table to parse source code.\autocite{go-faq-symbol}.
|
||||
|
||||
\subsection{Type-checking and AST-transformation}\label{sec:comp-type}
|
||||
|
||||
The second phase of compilation starts by converting the `syntax' package's
|
||||
AST, created in the first phase, to the compiler's AST representation. This
|
||||
is due to historical reasons, as gc's AST definition was carried over
|
||||
from the C implementation.
|
||||
|
||||
After the conversion, the AST is type-checked. Within the type-checking, there
|
||||
are also some additional steps included like checking for `declared and not used'
|
||||
variables and determining whether a function terminates.
|
||||
|
||||
After type-checking, transformations are applied on the AST. This includes
|
||||
eliminating dead code, inlining function calls and escape analysis, to name a few.
|
||||
What is also done in the transformation phase is rewriting built-in function
|
||||
calls, replacing for example a call to the built-in `append' with the necessary
|
||||
AST structure and runtime-calls to implement its functionality.
|
||||
|
||||
\subsection{SSA}
|
||||
|
||||
\newglossaryentry{ssa}{name=SSA,description={Single Static Assignment, an intermediate representation between the AST and the compiled binary that simplifies and improves compiler optimisations}}
|
||||
In the third phase, the AST is converted to \gls{ssa} form. SSA (Single Static Assignment) is `a
|
||||
lower-level intermediate representation with specific properties that make it
|
||||
easier to implement optimizations and to eventually generate machine code from
|
||||
it'\autocite{compiler-readme}.
|
||||
|
||||
The conversion consists of multiple `passes' through the SSA that
|
||||
apply machine-independent rules to optimise code. These generic
|
||||
rewrite rules are applied on every architecture and thus mostly
|
||||
concern expressions (for example replacing expressions with constant values and
|
||||
optimising multiplications), dead code elimination and removal of unneeded
|
||||
nil-checks.
|
||||
|
||||
\subsection{Generating machine code}
|
||||
|
||||
Lastly, in the fourth phase of the compilation, machine-specific
|
||||
SSA optimisations are applied. These may include:
|
||||
\begin{itemize}
|
||||
\item Rewriting generic values into their machine-specific variants
|
||||
(for example, on amd64, combining load-store operations)
|
||||
\item Dead-code elimination
|
||||
\item Pointer liveness analysis
|
||||
\item Removing unused local variables
|
||||
\end{itemize}
|
||||
|
||||
After generating and optimising the SSA form, the code is passed to the
|
||||
assembler, which replaces the so far generic instructions with
|
||||
architecture-specific machine code and writes out the final object file\autocite{compiler-readme}.
|
@ -0,0 +1,8 @@
|
||||
\section{Slice Helper Functions}
|
||||
|
||||
\input{chapters/31_builtins.tex}
|
||||
|
||||
|
||||
\section{Functional Check}\label{sec:funcheck-theory}
|
||||
|
||||
\input{chapters/32_funcheck.tex}
|
@ -0,0 +1,319 @@
|
||||
\subsection{Choosing the functions}
|
||||
|
||||
As mentioned in the introduction, the goal is to make functional programming in Go easier.
|
||||
To achieve this, the first task is to implement some helper functions for slices, similar to those that exist
|
||||
for lists in Haskell. To decide on which functions will be implemented, popular
|
||||
Haskell repositories on GitHub have been analysed. The popularity of repositories
|
||||
was decided to be based on their number of stars. Out of all Haskell projects
|
||||
on GitHub, the most popular are\autocite{github-popular-haskell}:
|
||||
|
||||
\begin{itemize}
|
||||
\item Shellcheck (koalaman/shellcheck\autocite{github-shellcheck}): A static analysis tool for shell scripts
|
||||
\item Pandoc (jgm/pandoc\autocite{github-pandoc}): A universal markup converter
|
||||
\item Postgrest (PostgREST/postgrest\autocite{github-postgrest}): REST API for any Postgres database
|
||||
\item Semantic (github/semantic\autocite{github-semantic}): Parsing, analyzing, and comparing source code across many languages
|
||||
\item Purescript (purescript/purescript\autocite{github-purescript}): A strongly-typed language that compiles to JavaScript
|
||||
\item Compiler (elm/compiler\autocite{github-elmcompiler}): Compiler for Elm, a functional language for reliable web apps
|
||||
\item Haxl (facebook/haxl\autocite{github-haxl}): A Haskell library that simplifies access to remote data, such as databases or web-based services
|
||||
\end{itemize}
|
||||
|
||||
In these repositories, the number of occurrences of popular list functions has
|
||||
been counted. The analysis does not differentiate between different `kinds' of
|
||||
functions. For example, `fold' includes all occurrences of \mintinline{haskell}|foldr|,
|
||||
\mintinline{haskell}|foldl| and \mintinline{haskell}|foldl'|; `map' also includes
|
||||
occurrences of \mintinline{haskell}|fmap|.
|
||||
Further, the analysis has not been done with any kind of AST-parsing.
|
||||
Rather, a simple `grep' has been used to find matches. This means that it is
|
||||
likely to contain some mismatches, for example in code comments. All in all,
|
||||
this analysis should only be an indicator of what functions are used most.
|
||||
|
||||
Running the analysis on the 7 repositories listed above, searching for a number
|
||||
of pre-selected list functions, indicates that the most used functions are `:'
|
||||
(cons), `map' and `fold', as shown in table~\ref{tab:occurrences-list-funcs}.
|
||||
|
||||
\begin{table}[htb]
|
||||
\centering
|
||||
\begin{tabular}{ll}
|
||||
\toprule
|
||||
`:' (cons) & 2912 \\
|
||||
\midrule
|
||||
map, fmap & 1873 \\
|
||||
\midrule
|
||||
foldr, foldl, foldl' & 303 \\
|
||||
\midrule
|
||||
filter & 262 \\
|
||||
\midrule
|
||||
reverse & 154 \\
|
||||
\midrule
|
||||
take & 108 \\
|
||||
\midrule
|
||||
drop & 81 \\
|
||||
\midrule
|
||||
maximum & 45 \\
|
||||
\midrule
|
||||
sum & 44 \\
|
||||
\midrule
|
||||
zip & 38 \\
|
||||
\midrule
|
||||
product & 15 \\
|
||||
\midrule
|
||||
minimum & 10 \\
|
||||
\end{tabular}
|
||||
\caption[Occurrences of list functions]{Occurrences of list functions\footnotemark}
|
||||
\label{tab:occurrences-list-funcs}
|
||||
\end{table}
|
||||
|
||||
\footnotetext{See Appendix~\ref{appendix:function-occurrences} for how these results have been achieved}
|
||||
|
||||
Based on this information, it has been decided to implement the map, cons, fold
|
||||
and filter functions into the Go compiler.
|
||||
|
||||
\subsection{Map}
|
||||
|
||||
The most used function in Haskell is map. The table~\ref{tab:occurrences-list-funcs}
|
||||
counts roughly 1250 occurrences of map, although around 600 of those are from `fmap'.
|
||||
fmap is part of the Functor type class\footnote{type classes
|
||||
in Haskell are similar to interfaces in imperative and object-oriented
|
||||
languages}, which is described as `a type that can be mapped over'\autocite{functor-wiki}.
|
||||
In general, a common analogy of the Functor type class is a box. A functor is like a box
|
||||
where a value can be put into and taken out again. fmap processes and transforms the item
|
||||
in that box. For lists, this process means iterating over and processing every item within that list.
|
||||
For the `Maybe' type it means `unpacking' the concrete value and processing it, or if there is
|
||||
no concrete value, returning `Nothing' instead.
|
||||
|
||||
The map function is exactly like fmap but only works on lists:
|
||||
|
||||
\begin{quote}
|
||||
\[map\] returns a list constructed by applying a function (the first argument) to all
|
||||
items in a list passed as the second argument\autocite{haskell-map}.
|
||||
\end{quote}
|
||||
|
||||
Some usage examples of map can be seen at~\ref{code:haskell-map}.
|
||||
|
||||
\begin{listing}
|
||||
\begin{haskellcode}
|
||||
Prelude> :t map
|
||||
map :: (a -> b) -> [a] -> [b]
|
||||
Prelude> :t fmap
|
||||
fmap :: Functor f => (a -> b) -> f a -> f b
|
||||
Prelude> map (*3) [1,2,3]
|
||||
[3,6,9]
|
||||
Prelude> fmap (*3) [1,2,3]
|
||||
[3,6,9]
|
||||
Prelude> map (++ " world") ["hello","goodbye"]
|
||||
["hello world","goodbye world"]
|
||||
Prelude> map show [1,2,3]
|
||||
["1","2","3"]
|
||||
\end{haskellcode}
|
||||
\caption{Example usage for map and fmap}\label{code:haskell-map}
|
||||
\end{listing}
|
||||
Due to missing polymorphism, map cannot be implemented easily in Go. While
|
||||
a specific definition of map would be
|
||||
\begin{gocode}
|
||||
func map(f func(int) string, []int) []string
|
||||
\end{gocode}
|
||||
this definition would only hold true for the specific type combination \mintinline{go}|int|
|
||||
and \mintinline{go}|string|. A more generic definition, similar to append,
|
||||
would be:
|
||||
\begin{gocode}
|
||||
func map(f func(Type1) Type2, []Type1) []Type2
|
||||
\end{gocode}
|
||||
For this to work, the function has to be implemented as a built-in into the compiler.
|
||||
|
||||
As there is already a `map' token in the Go compiler (for the map data type),
|
||||
the function will be called `fmap'. However, compared to Haskell's `fmap',
|
||||
Go's `fmap' only works on slices. This is due
|
||||
to the absence of an `functor'-like concept. Again, due to the absence of polymorphism,
|
||||
this cannot realistically built into the language in this context.
|
||||
|
||||
Nonetheless, to avoid possible naming confusions, the `map' function in Go will
|
||||
be called `fmap'.
|
||||
|
||||
In Go, the usage of `fmap' should result in making the program~\ref{code:fmap-usage-go}
|
||||
behave as shown\footnote{Printf's first argument, the
|
||||
verb `\%\#v', can be used to print the type (`\#') and the value (`v') of a
|
||||
variable\autocite{fmt-godoc}.}.
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%#v", fmap(strconv.Itoa, []int{1, 2, 3})) // []string{"1", "2", "3"}
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Example usage of map in Go}\label{code:fmap-usage-go}
|
||||
\end{listing}
|
||||
\subsection{Cons}
|
||||
|
||||
The name cons has been introduced by LISP, where it describes a record structure
|
||||
containing two components called the `car' (the `\textbf{c}ontents of the \textbf{a}ddress \textbf{r}egister')
|
||||
and the `cdr' (`\textbf{c}ontent of \textbf{d}ecrement \textbf{r}egister').
|
||||
Lists are built upon cons cells, where the `car' stores the element and `cdr' a
|
||||
pointer to the next cell - the next element of the list.
|
||||
This is why in Lisp, \mintinline{lisp}|(cons 1 (cons 2 (cons 3 (cons 4 nil))))| is equal to
|
||||
\mintinline{lisp}|(list 1 2 3 4)|. This list is also visualised in picture~\ref{fig:cons}.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\includegraphics[width=\linewidth]{../img/cons.png}
|
||||
\caption{Cons cells forming a list\autocite{cons-image-source}}
|
||||
\label{fig:cons}
|
||||
\end{figure}
|
||||
|
||||
The cons operator thus prepends an element to a list, effectively allocating a
|
||||
variable that contains the newly added element and a pointer to the `old' list.
|
||||
As a result, prepending to a list is computationally cheap, needing one allocation
|
||||
and one update.
|
||||
|
||||
In Haskell, the `name' of the cons function is the `:' operator.
|
||||
In Go, names for identifiers (which includes function names) underlie a simple
|
||||
rule:
|
||||
\begin{quote}
|
||||
An identifier is a sequence of one or more letters and digits. The first
|
||||
character in an identifier must be a letter.\autocite{spec-identifiers}
|
||||
\end{quote}
|
||||
|
||||
This rule forbids a function to be named `:'. Instead, the function could be
|
||||
named `cons'. However, Go already has a function to add to the end of a slice,
|
||||
`append'. Thus, adding to the beginning of a slice will be named `prepend'.
|
||||
Using prepend should be very similar to append. The behaviour of `prepend'
|
||||
should be equal to using `append' with a slight workaround\footnote{This workaround
|
||||
is to create a slice containing the prepended element, and expanding the
|
||||
destination slice with `...', as \mintinline{go}|append| is a variadic function}:
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%#v", append([]int{0}, []int{1,2,3}...)// []int{0, 1, 2, 3}
|
||||
fmt.Printf("%#v", prepend(0, []int{1, 2, 3})) // []int{0, 1, 2, 3}
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Example usage of prepend in go}\label{code:prepend-go}
|
||||
\end{listing}
|
||||
\subsection{Fold}\label{sec:fold}
|
||||
|
||||
Fold, sometimes also named `reduce' or `aggregate', is another higher-order function
|
||||
that is very commonly used in functional programming.
|
||||
|
||||
\begin{quote}
|
||||
fold refers to a family of higher-order functions that
|
||||
analyze a recursive data structure and through use of a given
|
||||
combining operation, recombine the results of recursively processing its
|
||||
constituent parts, building up a return value.\autocite{fold-wiki}
|
||||
\end{quote}
|
||||
|
||||
In other words, fold processes a list one by one and executes a `combining operation'
|
||||
on every element, for example summing up a list of integers.
|
||||
|
||||
The family of fold functions in Haskell consist of three different implementations of
|
||||
that definition: foldr, foldl and foldl'.
|
||||
The difference between foldr and foldl is hinted at their function headers:
|
||||
\begin{listing}
|
||||
\begin{haskellcode}
|
||||
Prelude Data.List> :t foldl
|
||||
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
|
||||
Prelude Data.List> :t foldr
|
||||
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
|
||||
\end{haskellcode}
|
||||
\caption{Function headers of the fold functions}
|
||||
\end{listing}
|
||||
The argument with type `b' is passed as the first argument to the foldl
|
||||
function, and as the second argument to foldr. As can be seen in the illustrations
|
||||
of foldl and foldr in~\ref{fig:fold}, the evaluation order of the two functions
|
||||
differ.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[scale=0.5]{../img/foldl.png}
|
||||
\includegraphics[scale=0.5]{../img/foldr.png}
|
||||
\caption{Folds illustrated\autocite{fold-wiki}}
|
||||
\label{fig:fold}
|
||||
\end{figure}
|
||||
|
||||
This is most obvious when using an example where the function is not associative:
|
||||
|
||||
\begin{listing}
|
||||
\begin{haskellcode}
|
||||
foldl (-) 0 [1..7]
|
||||
((((((0 - 1) - 2) - 3) - 4) - 5) - 6) - 7 = -28
|
||||
foldr (-) 0 [1..7]
|
||||
1 - (2 - (3 - (4 - (5 - (6 - (7 - 0)))))) = 4
|
||||
\end{haskellcode}
|
||||
\caption{foldr and foldl execution order}\label{code:foldr-example}
|
||||
\end{listing}
|
||||
In foldl, the accumulator (`0') is added to the left end of the list (prepended),
|
||||
while with foldr, the accumulator is added to the right end.
|
||||
For associative functions (e.g. `+') this does not make a difference, it does
|
||||
however for non-associative functions, as can be seen in the example~\ref{code:foldr-example}.
|
||||
|
||||
The difference between foldl and foldl' is more subtle:
|
||||
\begin{quote}
|
||||
foldl and foldl' are the same except for their strictness properties, so if both
|
||||
return a result, it must be the same.\autocite{fold-types}
|
||||
\end{quote}
|
||||
|
||||
The strictness property is only relevant if the function is lazy in its first argument.
|
||||
If this is the case, behavioural differences can be seen because foldl builds up a so
|
||||
called `execution path' (nesting the called functions), while foldl' executes these
|
||||
functions while traversing it already. An example of this is illustrated in
|
||||
Appendix~\ref{appendix:foldl-strictness}.
|
||||
|
||||
To keep things simpler, Go will only have its versions of foldl and foldr, which
|
||||
will both be strict --- the Haskell counterparts would thus be foldr and foldl'.\footnote{
|
||||
If the behaviour from the normal foldl function is required, a workaround can
|
||||
be applied in the Go version. See Appendix~\ref{appendix:foldl-go}}
|
||||
The usage of these fold-functions is equal to the Haskell versions, where foldl's
|
||||
arguments are switched in order.
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
sub := func(x, y int) int { return x - y }
|
||||
fmt.Printf("%v\n", foldr(sub, 100, []int{10, 20, 30})) // -80
|
||||
fmt.Printf("%v\n", foldl(sub, 100, []int{10, 20, 30})) // 40
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Example usage of foldr and foldl in go}\label{code:fold-go}
|
||||
\end{listing}
|
||||
\subsection{Filter}
|
||||
|
||||
The filter function is the conceptually simplest higher-order function.
|
||||
It takes a list and filters out all elements that are not matching
|
||||
a given predicate.
|
||||
This predicate usually is a function that takes said element and returns
|
||||
a boolean if it should be kept or filtered.
|
||||
|
||||
A simple example:
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
smallerThan5 := func(x int) bool {
|
||||
return x < 5
|
||||
}
|
||||
|
||||
fmt.Println(filter(smallerThan5, []int{1, 8, 5, 4, 7, 3})) // [1, 4, 3]
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Example usage of filter in Go}\label{code:filter-go}
|
||||
\end{listing}
|
@ -0,0 +1,446 @@
|
||||
To learn functional programming without a purely functional language, the
|
||||
developer needs to know which statements are functional and which
|
||||
would not exist or be possible in a purely functional language.
|
||||
For this reason, a `functional checker' should be created. It should work
|
||||
in a similar fashion to linters like `shellcheck', `go vet' or `gosimple'\footnote{
|
||||
A list of Go linters can be found in golangci-lint\autocite{golangci-lint}.
|
||||
}, with different `rules' that are reported upon. This tool will be named `funcheck'.
|
||||
|
||||
A set of rules should be compiled to identify common `non-functional' constructs
|
||||
which should then be reported upon.
|
||||
|
||||
The first step is to define a set of rules.
|
||||
|
||||
\subsection{Functional Purity}\label{sec:func-purity}
|
||||
|
||||
The goal of funcheck is to report every construct that can not be considered
|
||||
to be purely functional. As there is no agreed upon definition on what
|
||||
functional purity is\autocite{functional-controversy}, the first step is to
|
||||
define functional purity for the context of this thesis:
|
||||
|
||||
\begin{quote}
|
||||
A style of building the structure and elements of computer programs that
|
||||
treats all computation as the evaluation of mathematical functions. Purely
|
||||
functional programming may also be defined by forbidding changing state and
|
||||
mutable data.
|
||||
|
||||
Purely functional programming consists in ensuring that functions, inside
|
||||
the functional paradigm, will only depend on their arguments, regardless of
|
||||
any global or local state.\autocite{functional-purity-wiki}
|
||||
\end{quote}
|
||||
|
||||
This definition roughly brakes down into two parts; immutability and function
|
||||
purity. These parts and how they translate to Go will be discussed in the
|
||||
following chapters.
|
||||
|
||||
\subsection{Forbidding mutability and changing state}\label{sec:mutability}
|
||||
|
||||
\newglossaryentry{copy-by-value}{name=copy by value, description={Copy by value
|
||||
refers to the argument-passing style where the supplied arguments are
|
||||
copied. To pass references in Go, the developer needs to use pointers
|
||||
instead}}
|
||||
|
||||
Immutability refers to objects --- variables --- that are unchangeable. This means
|
||||
that their state cannot be modified after it's creation and first assignment.
|
||||
|
||||
Many programming languages provide features for making variables immutable.
|
||||
|
||||
In Rust for example, variables are immutable by default. To explicitly declare
|
||||
a mutable variable, the \mintinline{rust}|mut| keyword has to be used: \mintinline{rust}|let mut x = 5;|.
|
||||
|
||||
Java, in contrast, uses the \mintinline{java}|final| keyword:
|
||||
\begin{quote}
|
||||
A final variable can only be initialized once\autocite{final-java}
|
||||
\end{quote}
|
||||
`Only be initialized once' means that it cannot be reassigned later, effectively
|
||||
making that variable immutable.
|
||||
A caveat with Java's \mintinline{java}|final| keyword is that the immutability
|
||||
is only tied to the reference of that object, not
|
||||
it's contents (one may still change a field of an immutable object).
|
||||
|
||||
C and C++ have two features to achieve immutability, the \mintinline{c}|#define|
|
||||
preprocessor and the \mintinline{c}|const| keyword.
|
||||
The \mintinline{c}|#define| directive does not declare variables, but rather
|
||||
works in a similar way to string replacement at compile time. These directives
|
||||
can also be expressions. For example, this is a valid define statement:
|
||||
\begin{ccode}
|
||||
#define AGE (20/2)
|
||||
\end{ccode}
|
||||
|
||||
However, because \mintinline{c}|#define| is just text substitution, these
|
||||
identifiers are untyped.
|
||||
|
||||
The \mintinline{c}|const| qualifier specifies that a variable's value
|
||||
will not be changed, making it immutable. This is effectively the equivalent
|
||||
to Java's \mintinline{java}|final|.
|
||||
|
||||
Go has the \mintinline{go}|const| keyword too, and similar to C, constants can only be
|
||||
characters, strings, booleans or numeric values\footnote{In contrast to C, Go does
|
||||
have a boolean type}. A complex type --- a struct, interface or function --- cannot be constant.
|
||||
|
||||
This means that the programmer cannot write
|
||||
\mintinline{go}|const x = MyStruct{A: a, B: b}| to make a struct immutable.
|
||||
|
||||
A solution to making variables
|
||||
immutable is to explicitly not allow any mutations in a program, effectively
|
||||
disallowing all reassignments.
|
||||
To do this in Go, the simplest solution is
|
||||
to disallow the assignment operator \mintinline{go}|=|, and only
|
||||
allow it in combination with a declaration (\mintinline{go}|:=|).
|
||||
|
||||
This solution also has the side-effect that it makes it impossible to mutate
|
||||
existing, complex data structures like maps and slices\footnote{By not
|
||||
allowing assignments, elements can not be updated by
|
||||
\mintinline{go}|s[0] = "zero"| / \mintinline{go}|m["key"] = "value"|.}.
|
||||
|
||||
There are additional operators that work in a similar way to the assignment
|
||||
operator, for example \mintinline{go}|++|, \mintinline{go}|--| and
|
||||
\mintinline{go}|+=|. These are all `hidden' assignments and will need to
|
||||
be reported upon too.
|
||||
|
||||
Go being a \gls{copy-by-value} language, mutating existing variables
|
||||
across functions works by passing pointers to these variables\footnote{
|
||||
An example for this can be found in Appendix~\ref{appendix:mutation}.}.
|
||||
When removing the regular assignment operators, the usage of pointers becomes
|
||||
second nature, as it cannot be updated either way.
|
||||
Technically, the only advantage that pointers have left, is that less memory has
|
||||
to be copied\footnote{The pointer still has to be passed, and thus copied, to the
|
||||
called function} if a variable's type is big enough\footnote{For example, it is
|
||||
cheaper to copy an \mintinline{go}|int32| than to take address of it, copy that
|
||||
(64 bit, on most systems) address and dereferencing it again}.
|
||||
This optimisation is left to the developer.
|
||||
|
||||
In functional languages in contrast, this optimisation is usually left to the compiler
|
||||
and there are no pointers at all in the language\footnote{Except for things like the `foreign
|
||||
function interface' (FFI) in Haskell, which allows to call C functions from Haskell code}.
|
||||
|
||||
A feature not discussed so far is shadowing. Shadowing a variable in a different
|
||||
scope is still possible by redeclaring it. As expected, even with the above
|
||||
mentioned limitations, the rules for shadowing variables do not change. This
|
||||
can be seen in Appendix~\ref{appendix:shadowing}.
|
||||
|
||||
\subsection{Pure functions}
|
||||
|
||||
A pure function is a function where the return value is only dependent on
|
||||
the arguments. With the same arguments, the function should always return
|
||||
the same values. The evaluation of the function is also not allowed to have
|
||||
any `side effects', meaning that it should not mutate non-local variables or
|
||||
references\footnote{This also includes `local static' variables, but Go does
|
||||
not have a way to make variables `static'.}.
|
||||
|
||||
As discussed in the last Chapter~\ref{sec:mutability}, if reassignments
|
||||
are not allowed, mutating global variables is not possible either.
|
||||
|
||||
If global state cannot be changed, it also cannot influence a function's return values.
|
||||
|
||||
As such, forbidding reassignments is an extremely simple solution to
|
||||
ensure functional purity within the program. However, there is one
|
||||
important aspect that the solution does not solve: interacting with the outside world.
|
||||
|
||||
\subsection{IO}
|
||||
Network and file access, time, randomness, user input and sensors are all examples
|
||||
of IO, things that interact with state outside of the program.
|
||||
|
||||
Input and Output in a program --- through whether channel it may be --- is
|
||||
impure by design. Due to this, the Haskell language authors faced a difficult
|
||||
challenge; how to add impure functions to an otherwise completely pure
|
||||
language.
|
||||
|
||||
In a pure language, functions only depend on their arguments, have
|
||||
no side effects and return a value computed from these arguments. Because
|
||||
of this, the execution order of functions is not relevant, a function
|
||||
can be executed whenever its return value is needed.
|
||||
Due to this, the compiler can optimise the code, calling the functions
|
||||
in a specific (or in no specific) order or omitting calls if their return
|
||||
values are unused.
|
||||
|
||||
However, impure IO functions introduce some difficulties.
|
||||
|
||||
For example, one would expect that it is possible to write a trivial `writeFile'
|
||||
function that takes a filename and a string with the contents for that file with
|
||||
the function definition roughly being:
|
||||
|
||||
\begin{haskellcode}
|
||||
writeFile :: String -> String
|
||||
\end{haskellcode}
|
||||
Supposed that this function could be called with
|
||||
\mintinline{haskell}|writeFile "hello.txt" "Hello World"|. The function
|
||||
does not return anything, or its return value, for example the number
|
||||
of bytes that have been written to the file, is discarded on the call site.
|
||||
|
||||
As there is no dependency on that function anymore, the compiler assumes that
|
||||
this function does not need to be executed at all --- it still expects all
|
||||
functions to be pure (even if this is not the case in this example).
|
||||
Being able to follow the purity guarantees, the compiler emits the call
|
||||
to the writeFile function and the file never gets written.
|
||||
|
||||
Similarly, for getting user input:
|
||||
\begin{haskellcode}
|
||||
getChar :: Char
|
||||
\end{haskellcode}
|
||||
If this function is called multiple times, the compiler may reorder the
|
||||
execution of these calls, again, because it expects the functions to be
|
||||
pure, so the execution order should not matter:
|
||||
\begin{haskellcode}
|
||||
get2Chars = [getChar, getChar]
|
||||
\end{haskellcode}
|
||||
However, in this example, execution order is extremely important.
|
||||
|
||||
To solve this problem, Haskell introduced the IO monad, in which
|
||||
all functions are wrapped with a `RealWorld' argument. So the
|
||||
\mintinline{haskell}|getChar| example would be written as\footnote{
|
||||
This is not the actual function header for getChar, only an illustration.
|
||||
The actual function header of getChar is \mintinline{haskell}|getChar :: IO Char|.
|
||||
}:
|
||||
|
||||
\begin{haskellcode}
|
||||
getChar :: RealWorld -> (Char, RealWorld)
|
||||
\end{haskellcode}
|
||||
This can be read as `take the current RealWorld, do something with it,
|
||||
and return a Char and a (possibly changed) RealWorld'\autocite{haskell-io}.
|
||||
|
||||
With this solution, the compiler cannot reorder or omit IO actions, ensuring
|
||||
the correct execution of the program.
|
||||
|
||||
The IO monad is a sophisticated solution to work around the purity guarantees and
|
||||
enables Haskellers to use impure functions in an otherwise completely pure language.
|
||||
Put another way: the IO monad exists because of compiler optimisations, which in turn are
|
||||
based on the assumption that everything is pure.
|
||||
|
||||
In Go, the compiler does not and can not make this assumption. There also
|
||||
is no `pure' keyword to mark a function as such. For that reason, the
|
||||
compiler cannot optimise as aggressively, and the whole IO problem does
|
||||
not exist. However, in the context of this thesis, interacting with the outside world ---
|
||||
using IO-functions --- means that functional purity cannot be guaranteed anymore.
|
||||
Haskell's solution to this issue is relatively complex, and while it may be applicable
|
||||
to Go, it would require massively rewriting Go's standard library. Furthermore,
|
||||
the IO monad does not make impure functions magically pure. Rather, it
|
||||
clearly marks functions as impure.
|
||||
|
||||
Another issue, apart from the amount of work that would be needed, is that
|
||||
the goal is to provide an easy introduction to functional programming.
|
||||
IO, as one may see in these examples, is one of the hardest topics.
|
||||
|
||||
For those reasons, although it may violate the functional purity requirements,
|
||||
the underlying issue with IO will be ignored. This means that the programmer
|
||||
will still be able to use IO functions as in regular Go programs.
|
||||
|
||||
\subsection{Assignments in Go}
|
||||
|
||||
As described in Section~\ref{sec:mutability}, the only check that needs
|
||||
to be done is that there are no reassignments in the code. There are a few
|
||||
exceptions to this rule, which will be covered later. First, it is important to have
|
||||
a few examples and test cases to be clear on what should be reported.
|
||||
|
||||
To declare variables in Go, the \mintinline{go}|var| keyword is used.
|
||||
The var keyword needs to be followed by one or more identifiers and maybe
|
||||
types or the initial value. These are all valid declarations:
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
var i int
|
||||
var U, V, W float64
|
||||
var k = 1
|
||||
var x, y float32 = -1, -2
|
||||
\end{gocode}
|
||||
\caption{Go Variable Declarations}
|
||||
\end{listing}
|
||||
What can be seen in this example is that the type of the variable can be
|
||||
deducted automatically\footnote{The compiler will infer the type at compile
|
||||
time. That operation is always successful, although it may not be what
|
||||
the programmer desires. For example, \mintinline{go}|var x = 5| will
|
||||
always result in \mintinline{go}|x| to be of type \mintinline{go}|int|.},
|
||||
or be specified explicitly.
|
||||
|
||||
However, the notation \mintinline{go}|var k = 1| is seldomly seen. This
|
||||
is due to the fact that there is a second way to declare and initialise
|
||||
variables to a specific value, the `short variable declaration' syntax:
|
||||
|
||||
\begin{gocode}
|
||||
k := 1
|
||||
\end{gocode}
|
||||
\begin{quote}
|
||||
It is shorthand for a regular variable declaration with initializer expressions but no types:
|
||||
|
||||
\begin{bnfcode}
|
||||
"var" IdentifierList = ExpressionList .
|
||||
\end{bnfcode}
|
||||
\autocite{short-hand-decl}
|
||||
\end{quote}
|
||||
|
||||
In practice, the short variable declaration is used more often due to the
|
||||
fact that there is no need to specify the type of the variable manually and
|
||||
is shorter than its counterpart \mintinline{go}|var x = y|.
|
||||
|
||||
Go also has increment, decrement and various other operators to reassign
|
||||
variables. Most of the operators that take a `left' and `right' expression
|
||||
also have a short-hand notation to reassign the value back to the variable,
|
||||
for example\footnote{This is a non-exhaustive list of operators. A complete listing
|
||||
of operators can be found in the Go language spec\autocite{spec-operators}.}:
|
||||
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
var x = 5
|
||||
x += 5 // equal to x = x + 5
|
||||
x %= 3 // equal to x = x % 3
|
||||
x <<= 2 // equal to x = x << 2 (bit-shift)
|
||||
\end{gocode}
|
||||
\caption{Go Assignment Operators}
|
||||
\end{listing}
|
||||
All these operators are reassigning values and should be reported.
|
||||
|
||||
However, as mentioned in the beginning of this chapter, there are a few exceptions
|
||||
to this rule:
|
||||
|
||||
\subsubsection{Function Assignments}\label{sec:func-reassign}
|
||||
|
||||
A variable declaration brings an identifier into scope. The Go Language Specification
|
||||
defines this scope according to the following rule:
|
||||
\begin{quote}
|
||||
The scope of a constant or variable identifier declared inside a function begins
|
||||
at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable
|
||||
declarations) and ends at the end of the innermost containing block.
|
||||
\autocite{spec-scope}\end{quote}
|
||||
|
||||
This definition holds an important caveat for function definitions:
|
||||
\begin{quote}
|
||||
The scope [...] begins \textbf{at the end} of the ConstSpec or VarSpec [...].
|
||||
\end{quote}
|
||||
|
||||
This means that when defining a function, the functions identifier is not
|
||||
in scope within that function:
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
func X() {
|
||||
f := func() {
|
||||
f() // <- 'f' is not in scope yet.
|
||||
}
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Go scoping issue with recursive functions}
|
||||
\end{listing}
|
||||
In the above example, the function \mintinline{go}|f| cannot be called recursively. This is due
|
||||
to the fact that the identifier \mintinline{go}|f| is not in scope at \mintinline{go}|f|'s definition.
|
||||
Instead, it only enters scope after the closing brace of function \mintinline{go}|f|.
|
||||
|
||||
This cannot be changed within the compiler without touching a lot of the scoping
|
||||
rules and changing these scoping rules may have unintended side effects. To exemplify,
|
||||
a naive solution to the issue would be to bring the identifier into scope right after the
|
||||
assignment operator (\mintinline{go}|=|). However, this would introduce further
|
||||
edge cases, as for example \mintinline{go}|x := x|, and extra measures would have
|
||||
to be taken to make such constructs illegal again.
|
||||
|
||||
However, there is a simple, although verbose solution to this problem: The function
|
||||
has to be declared in advance:
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
func X() {
|
||||
var f func() // f enters scope after this line
|
||||
f = func() {
|
||||
f() // this is allowed, as f is in scope
|
||||
}
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Fixing the scope issue on recursive functions}
|
||||
\end{listing}
|
||||
This exception will need to be allowed by the assignment checker, as recursive functions
|
||||
are one of the building blocks in functional programming.
|
||||
|
||||
\subsubsection{Multiple Assignments}\label{sec:multi-assign}
|
||||
|
||||
\newglossaryentry{ebnf}{name=EBNF, description={Extended Backus-Naur Form, an extended version of the `Backus-Naur
|
||||
Form, a notation technique to describe programming language syntax}}
|
||||
|
||||
As discussed at the beginning of this Chapter, the short variable declaration brings
|
||||
a variable into scope and assigns it to a given expression (value). It is also
|
||||
possible to declare multiple variables at once, as can be seen in the \gls{ebnf}
|
||||
notation:
|
||||
\begin{listing}
|
||||
\begin{bnfcode}
|
||||
ShortVarDecl = IdentifierList ":=" ExpressionList .
|
||||
\end{bnfcode}
|
||||
\end{listing}
|
||||
This clearly shows that both left- and right-hand side take a list, meaning one or more
|
||||
elements, making a statement like \mintinline{go}|x, y := 1, 2| valid\footnote{This also
|
||||
holds true for the \mintinline{go}|var| keyword}.
|
||||
|
||||
However, there is an important note to be found in the language specification:
|
||||
|
||||
\begin{quote}
|
||||
Unlike regular variable declarations, a short variable declaration may redeclare
|
||||
variables provided they were originally declared earlier in the same block (or
|
||||
the parameter lists if the block is the function body) with the same type, and
|
||||
at least one of the non-blank\footnote{The blank identifier (\mintinline{go}|_|)
|
||||
discards a value; \mintinline{go}|x, _ := f()|} variables is new. As a consequence, redeclaration
|
||||
can only appear in a multi-variable short declaration. Redeclaration does not
|
||||
introduce a new variable; it \textbf{just assigns a new value to the original}\autocite{short-hand-decl}.
|
||||
\begin{gocode}
|
||||
field1, offset := nextField(str, 0)
|
||||
field2, offset := nextField(str, offset) // redeclares offset
|
||||
\end{gocode}
|
||||
\end{quote}
|
||||
|
||||
This should not be allowed, as the programmer can introduce mutation by redeclaring
|
||||
already existing variables in a short variable declaration.
|
||||
|
||||
\subsubsection{For Loops}
|
||||
|
||||
Although the aforementioned rules cover almost every case of reassignment, they miss
|
||||
one of the most important parts: For loops.
|
||||
|
||||
In Go, there is only one looping construct, the \mintinline{go}|for| loop. However, it can
|
||||
be used in roughly four different ways:
|
||||
|
||||
The infinite loop:
|
||||
\begin{gocode}
|
||||
for {
|
||||
// ...
|
||||
}
|
||||
\end{gocode}
|
||||
The while loop:
|
||||
\begin{gocode}
|
||||
for x == true { // boolean expression
|
||||
// ...
|
||||
}
|
||||
\end{gocode}
|
||||
The regular C-style for loop:
|
||||
\begin{gocode}
|
||||
for x := 0; x < 10; x++ {
|
||||
// ...
|
||||
}
|
||||
\end{gocode}
|
||||
And the range loop, which allows to range (iterate) over slices, maps and channels\footnote{Channels
|
||||
have not been discussed in this thesis, but are an important building block in Go's concurrency
|
||||
features. They work similarly to Unix Pipes (`|'), in that they allow to send data in and receive
|
||||
it on the other `side'. A \mintinline{go}|range| receives as long as the channel is not closed.}:
|
||||
\begin{gocode}
|
||||
for idx, elem := range []int{1,8,5,4} {
|
||||
// ...
|
||||
}
|
||||
\end{gocode}
|
||||
Range returns two values, both of which can be omitted\footnote{The values can be omitted
|
||||
by using the blank identifier `\_'. Skipping the index is thus
|
||||
\mintinline{go}|_, elem := range s|, while skipping the value with
|
||||
\mintinline{go}|i, _ := range s| which is equal to just \mintinline{go}|i := range s|.}. When
|
||||
ranging over a slice, the index and a copy of the element at that index is returned. For
|
||||
maps, the key and a copy of the corresponding value is returned.
|
||||
|
||||
Except of the C-style for loop, the loops do not have a reassignment statement when
|
||||
converted to the AST. For this reason, they would not be detected by the previously defined rule and thus
|
||||
the programmer could use them.
|
||||
However, issues emerge when having a closer look at the loop constructs.
|
||||
|
||||
The \mintinline{go}|range| loop needs to keep track of the element, and it does so (internally)
|
||||
by using a pointer. This results in a (internal) reassignment.
|
||||
|
||||
The regular C-style for loop and the `while' loop do not work at all without reassignments.
|
||||
Although in the while-loop the reassignment happens at another place, most probably within the loop,
|
||||
it is easier for the user if a report on the loop construct is generated instead.
|
||||
|
||||
The infinite loop does not contain any reassignments and is thereby be legal by the
|
||||
definition of the reassignment rule. Because of this, it should not be reported.
|
||||
|
||||
All other for-loops need to be reported as they contain internal reassignments.
|
@ -0,0 +1,7 @@
|
||||
\section{Implementing the new built-in functions}
|
||||
|
||||
\input{chapters/41_builtins.tex}
|
||||
|
||||
\clearpage
|
||||
\section{Functional Check}
|
||||
\input{chapters/45_funcheck.tex}
|
@ -0,0 +1,120 @@
|
||||
\subsection{Required Steps}
|
||||
|
||||
Adding a built-in function to the Go language requires a few more steps than just
|
||||
adding support within the compiler. While it would technically be enough to
|
||||
support the translation between Go code and the compiled binary, there would be
|
||||
no visibility for a developer that there is a function which could be used.
|
||||
For a complete implementation, the following steps are necessary:
|
||||
\begin{itemize}
|
||||
\item Adding the Godoc\autocite{godoc} that describes the function and it's usage
|
||||
\item Adding type-checking support in external packages for tools like
|
||||
Gopls\footnote{Gopls is Go's official language server implementation\autocite{gopls}.}
|
||||
\item Adding the implementation within the internal\footnote{
|
||||
`internal' packages can only be imported by other packages that
|
||||
are rooted at the parent of the `internal' directory. It is used to
|
||||
make specific packages not importable in order to decrease potential API surface\autocite{internal-packages}.
|
||||
}
|
||||
package of the compiler
|
||||
\begin{itemize}
|
||||
\item Adding the \gls{ast} node type
|
||||
\item Adding type-checking for that node type
|
||||
\item Adding the AST traversal (`walk') for that node type, translating it
|
||||
to AST nodes that the compiler already knows and can translate
|
||||
to built-in runtime-calls or \gls{ssa}
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
|
||||
The Go source code that is relevant for this thesis can be classified into three different
|
||||
types. One is the Godoc --- the documentation for the new built-in functions. The
|
||||
other two are the `public' and the `private' implementation of these built-ins.
|
||||
|
||||
The `private' implementation is located within the
|
||||
\textit{src/cmd/compile/internal} package\autocite{internal-packages}. Because it
|
||||
is an internal package, it can only
|
||||
be used by the packages in \textit{src/cmd/compile}, which contain the
|
||||
implementation of the compiler itself.
|
||||
|
||||
When calling
|
||||
\begin{bashcode}
|
||||
$> go build .
|
||||
\end{bashcode}
|
||||
the compiler is invoked indirectly
|
||||
through the main `go' binary. To directly invoke the compiler,
|
||||
\begin{bashcode}
|
||||
$> go tool compile
|
||||
\end{bashcode}
|
||||
can be used.
|
||||
|
||||
Everything that is not in \textit{src/cmd/compile} is referred to as the `public'
|
||||
part of the compiler in this thesis. The `public' parts are used by external
|
||||
tools, for example Gopls, for type-checking, source code validation and
|
||||
analysis.
|
||||
|
||||
\subsection{Adding the Godoc}
|
||||
In Go, documentation is generated directly from comments within the source code
|
||||
\autocite{godoc}. This also applies to built-in functions in the compiler, which
|
||||
have a function stub to document their behaviour\autocite{godoc-builtin}, but
|
||||
no implementation, as that is done in the compiler\autocite{builtin-impl}.
|
||||
|
||||
The documentation for built-ins should be as short and precise as possible.
|
||||
The usage of `Type' and `Type1' has been decided based on other built-ins
|
||||
like `append' and 'delete'.
|
||||
The function headers are derived from their Haskell counterparts, adjusted
|
||||
to the Go nomenclature.
|
||||
|
||||
\begin{code}
|
||||
\gofilerange{../work/go/src/builtin/builtin.go}{begin-newbuiltins}{end-newbuiltins}%
|
||||
\caption{Godoc for the new built-in functions\autocite{new-builtins-godoc}}
|
||||
\end{code}
|
||||
\subsection{Public packages}
|
||||
|
||||
To enable tooling support for the new built-in functions, they have to be
|
||||
registered in the `go/*' packages. The only package that is affected by new
|
||||
built-ins is `go/types'.
|
||||
|
||||
\begin{quote}
|
||||
Note that the `go/*` family of packages, such as `go/parser` and `go/types`,
|
||||
have no relation to the compiler. Since the compiler was initially written in C,
|
||||
the `go/*` packages were developed to enable writing tools working with Go code,
|
||||
such as `gofmt` and `vet`.\autocite{compiler-readme}
|
||||
\end{quote}
|
||||
|
||||
In the `types' package, the built-ins have to be registered as such and as
|
||||
`predeclared' functions:
|
||||
|
||||
\begin{code}
|
||||
\gofilerange{../work/go/src/go/types/universe.go}{start-builtin}{end-builtin}%
|
||||
\gofilerange{../work/go/src/go/types/universe.go}{start-predeclared}{end-predeclared}%
|
||||
\caption{Registering new built-in functions\autocite{new-builtins-universe}}
|
||||
\end{code}
|
||||
This registration defines the type of the built-in --- they are all expressions,
|
||||
as they return a value --- and the number of arguments.
|
||||
After that, the type-checking and its associated tests have been implemented, but
|
||||
are not shown here. The implementation can be located in the `src/go/types' package
|
||||
in the files `builtins.go', `builtins\_test.go' and `universe.go' See the git
|
||||
diff\autocite{ba-go1-14-thesis-diff} to view the changes that have been made.
|
||||
|
||||
This concludes the type-checking for external tools.
|
||||
`gopls' can be compiled against these changed public packages\footnote{
|
||||
See Appendix~\ref{appendix:build-gopls} for instructions on how to build Gopls.
|
||||
} and will then return errors if the wrong types are used. For example, when trying
|
||||
to prepend an integer to a string slice:
|
||||
|
||||
\begin{gocode}
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println(prepend(3, []string{"hello", "world"}))
|
||||
}
|
||||
\end{gocode}
|
||||
|
||||
Gopls will report a type-checking error:
|
||||
\begin{bashcode}
|
||||
$ gopls check main.go
|
||||
/tmp/playground/main.go:6:22-23: cannot convert 3 (untyped int constant) to string
|
||||
\end{bashcode}
|
||||
|
||||
\subsection{Private packages}
|
||||
\input{chapters/42_functions.tex}
|
@ -0,0 +1,203 @@
|
||||
In the private packages - the actual compiler - the expressions have to be
|
||||
type-checked, ordered and transformed.
|
||||
|
||||
The type-checking process is similar to the one executed for external tools.
|
||||
Furthermore, during the type-checking
|
||||
process, the built-in function's return types are set and node types
|
||||
may be converted, if possible and necessary.
|
||||
An operation may expect it's arguments to be in \mintinline{go}|node.Left|
|
||||
and \mintinline{go}|node.Right|, which means type-checking will also need
|
||||
to move the argument nodes from their default location in
|
||||
\mintinline{go}|node.List| to \mintinline{go}|node.Left| and
|
||||
\mintinline{go}|node.Right|.
|
||||
|
||||
Ordering ensures the evaluation order and re-orders expressions. All of
|
||||
the new built-in functions will be evaluated left-to-right and there are no
|
||||
special cases to handle.
|
||||
|
||||
Transforming means changing the AST nodes from the built-in operation to
|
||||
nodes that the compiler knows how to translate to SSA. The actual algorithm
|
||||
that these functions use cannot be implemented in normal Go code, they have to be
|
||||
translated directly to AST nodes and statements.
|
||||
|
||||
There are more steps to compiling Go code, for example escape-checking,
|
||||
SSA conversion and a lot of optimisations. These are not necessary to
|
||||
implement and do not have a direct relation to the new built-ins.
|
||||
|
||||
The algorithms and part of the implementations for the built-in
|
||||
functions are covered in the following chapters\footnote{
|
||||
To see the full implementation, the git diff can be viewed\autocite{ba-go1-14-thesis-diff}.
|
||||
}.
|
||||
|
||||
\subsubsection{fmap}\label{ch:impl-fmap}
|
||||
|
||||
To make the implementation in the AST easier, the algorithm will first be
|
||||
developed in Go, and then translated. Implementing fmap in Go is relatively
|
||||
simple:
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
func fmap(fn func(Type) Type1, src []Type) (dest []Type1) {
|
||||
for _, elem := range src {
|
||||
dest = append(dest, fn(elem))
|
||||
}
|
||||
return dest
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{fmap implementation in Go}\label{code:fmap-go}
|
||||
\end{listing}
|
||||
However, there is room for improvement within that function. Instead
|
||||
of calling \mintinline{go}|append| at every iteration of the loop, the slice can
|
||||
be allocated with \mintinline{go}|make| at the beginning of the function. Thus,
|
||||
calls to grow the slice at runtime can be saved.
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
func fmap(fn func(Type) Type1, src []Type) []Type1 {
|
||||
dest := make([]Type1, len(src))
|
||||
for i, elem := range src {
|
||||
dest[i] = fn(elem)
|
||||
}
|
||||
return dest
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Improved implementation of fmap}\label{code:fmap-go-improved}
|
||||
\end{listing}
|
||||
This algorithm can be translated to the following AST node:
|
||||
|
||||
\begin{code}
|
||||
\gofilerange{../work/go/src/cmd/compile/internal/gc/walk.go}{start-fmap-header}{end-fmap-header}%
|
||||
\caption{fmap AST translation\autocite{fmap-walk-implementation}}
|
||||
\end{code}
|
||||
|
||||
The full AST code is not displayed here as, although the algorithm is simple, the AST translation
|
||||
is not as concise and more than 10 times the size. A demonstration on how a translation looks like
|
||||
will be introduced in Section~\ref{ch:ast-traversal}.
|
||||
The full implementation of this function is referenced in the code block's caption.
|
||||
|
||||
\subsubsection{prepend}
|
||||
|
||||
The general algorithm for `prepend' is:
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
func prepend(elem Type, slice []Type) []Type {
|
||||
dest := make([]Type, 1, len(src)+1)
|
||||
dest[0] = elem
|
||||
return append(dest, slice...)
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{prepend implementation in Go}
|
||||
\end{listing}
|
||||
The call to \mintinline{go}|make(...)| creates a slice with the length of 1 and the capacity
|
||||
to hold all elements of the source slice, plus one. By allocating the slice with the full
|
||||
length, another slice allocation within the call to \mintinline{go}|append(...)| is saved.
|
||||
The element to prepend is added as the first element of the slice, and append will then
|
||||
copy the `src' slice into `dest'.
|
||||
|
||||
The implementation within `walkprepend' reflects these lines of Go code, but
|
||||
as AST nodes:
|
||||
|
||||
\begin{code}
|
||||
\gofilerange{../work/go/src/cmd/compile/internal/gc/walk.go}{start-prepend-header}{end-prepend-header}%
|
||||
\caption{prepend AST translation\autocite{prepend-walk-implementation}}
|
||||
\end{code}
|
||||
\subsubsection{foldr and foldl}
|
||||
|
||||
As outlined in Chapter~\ref{sec:fold}, there will be two fold functions;
|
||||
foldr and foldl. foldr behaves exactly like its Haskell counterpart,
|
||||
while foldl behaves like foldl' in Haskell.
|
||||
|
||||
While the fold algorithms are most obvious when using recursion, due to
|
||||
performance considerations, an imperative implementation has been chosen:
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
func foldr(fn func(Type, Type1) Type1, acc Type1, slice []Type) Type1 {
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
acc = fn(s[i], acc)
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
func foldl(fn func(Type1, Type) Type1, acc Type1, slice []Type) Type1 {
|
||||
for i := 0; i < len(s); i++ {
|
||||
acc = f(acc, s[i])
|
||||
}
|
||||
return acc
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{fold implementation in Go}
|
||||
\end{listing}
|
||||
The code further clarifies the differences between the two different folds;
|
||||
the slice is processed in reverse order for foldr (as it would be if this
|
||||
algorithm would have been implemented with recursion), and the order of
|
||||
arguments to the fold function is switched.
|
||||
|
||||
The AST walk translates fold to:
|
||||
\begin{code}
|
||||
\gofilerange{../work/go/src/cmd/compile/internal/gc/walk.go}{start-fold-header}{end-fold-header}%
|
||||
\caption{fold AST translation\autocite{fold-walk-implementation}}
|
||||
\end{code}
|
||||
\subsubsection{filter}\label{ch:impl-filter}
|
||||
|
||||
Being a slice-manipulating function, filter also needs to traverse the whole
|
||||
slice in a for-loop. However, compared to the other newly built-in functions,
|
||||
the size for the target slice is unknown until all items have been traversed,
|
||||
which is why filter does not allow for the same optimisations as the other
|
||||
functions.
|
||||
|
||||
\begin{code}
|
||||
\begin{gocode}
|
||||
func filter(f func(Type) bool, s []Type) (dst []Type) {
|
||||
for i := range s {
|
||||
if f(s) {
|
||||
dst = append(dst, s[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{filter implementation in Go}
|
||||
\end{code}
|
||||
And the same algorithm, but translated to AST statements:
|
||||
|
||||
\begin{code}
|
||||
\gofilerange{../work/go/src/cmd/compile/internal/gc/walk.go}{start-filter-header}{end-filter-header}%
|
||||
\caption{filter AST translation\autocite{filter-walk-implementation}}
|
||||
\end{code}
|
||||
|
||||
\subsubsection{Writing the AST traversal}\label{ch:ast-traversal}
|
||||
|
||||
The previous chapters have all shown the function headers of the `walk' functions
|
||||
that are used to traverse and rewrite the new built-ins. To illustrate how the
|
||||
actual implementation of such an algorithm looks like in these functions, we
|
||||
provide a small example here. The full implementation of these algorithms can
|
||||
be viewed at the git diff\autocite{ba-go1-14-thesis-diff}.
|
||||
|
||||
This demonstration shows the translation of the statement
|
||||
\begin{gocode}
|
||||
filtered := make([]T, 0)
|
||||
\end{gocode}
|
||||
into AST nodes, or rather the construction of these AST nodes.
|
||||
The type is simply a placeholder, as the AST construction uses the source slice's
|
||||
type. This source slice is another AST node of which the type can be obtained from.
|
||||
|
||||
\begin{code}
|
||||
\begin{gocode}
|
||||
// create the AST node for the first argument that is
|
||||
// being passed to `make', the type:
|
||||
makeType := nod(OTYPE, nil, nil)
|
||||
makeType.Type = source.Type // use the type of the slice
|
||||
|
||||
// create the make(...) AST node
|
||||
makeDest := nod(OMAKE, nil, nil)
|
||||
// add the arguments (the type and an int constant 0
|
||||
makeDest.List.Append(makeType, nodintconst(0)) // make([]<T>, 0))
|
||||
|
||||
// create the "variable" where the result of make will be stored
|
||||
filtered := temp(source.Type)
|
||||
// the final AST node that contains the statement
|
||||
// filtered = make([]<T>, 0)
|
||||
final := nod(OAS, filtered, makeDest))
|
||||
\end{gocode}
|
||||
\caption{Illustrating the difference between Go code and it's AST code}
|
||||
\end{code}
|
@ -0,0 +1,270 @@
|
||||
As discussed in Chapter~\ref{sec:funcheck-theory}, to assist writing purely
|
||||
functional code, a linter needs to be implemented that detects reassignments within
|
||||
a Go program.
|
||||
|
||||
To get a grasp on the issues this linter should report, the first step
|
||||
is to capture some examples, cases that should be matched against.
|
||||
|
||||
\subsection{Examples}
|
||||
|
||||
The simplest cases are standalone reassignments and assignment operators:
|
||||
\begin{gocode}
|
||||
x := 5
|
||||
x = 6 // forbidden
|
||||
// or
|
||||
var y = 5
|
||||
y = 6 // forbidden
|
||||
y += 6 // forbidden
|
||||
y <<= 2 // forbidden
|
||||
y++ // forbidden
|
||||
\end{gocode}
|
||||
|
||||
Where the statements with a \mintinline{go}|// forbidden
|
||||
|comment should be reported.
|
||||
|
||||
Adding block scoping to this, shadowing the old variable needs to be allowed:
|
||||
\begin{gocode}
|
||||
x := 5
|
||||
{
|
||||
x = 6 // forbidden, changing the old value
|
||||
x := 6 // allowed, as this shadows the old variable
|
||||
}
|
||||
\end{gocode}
|
||||
|
||||
What should be illegal is to declare the variable first and then assign a
|
||||
value to it:
|
||||
\begin{gocode}
|
||||
var x int
|
||||
x = 6 // forbidden
|
||||
\end{gocode}
|
||||
|
||||
The exception here are functions, as they need to be declared first in order
|
||||
to recursively call them:
|
||||
\begin{gocode}
|
||||
var f func()
|
||||
f = func() {
|
||||
f()
|
||||
}
|
||||
\end{gocode}
|
||||
|
||||
Furthermore, the linter also needs to be able to handle multiple variables
|
||||
at once:
|
||||
\begin{gocode}
|
||||
var f func()
|
||||
x, f, y := 1, func() { f() }, 2
|
||||
\end{gocode}
|
||||
|
||||
Loops should be reported too, as they are using reassignments internally:
|
||||
\begin{gocode}
|
||||
for i := 0; i < 5; i++ { // forbidden
|
||||
for i != 3 { // forbidden
|
||||
for { // allowed
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
\end{gocode}
|
||||
|
||||
All the aforementioned examples and more can be found in the test cases for funcheck\autocite{funcheck-examples}.
|
||||
|
||||
\subsection{Building a linter}
|
||||
|
||||
The Go ecosystem already provides an official library for building code analysis tools,
|
||||
the `analysis' package from the Go Tools repository\autocite{go-analysis}. With this package,
|
||||
implementing a static code analyser is reduced to writing the actual AST node analysis.
|
||||
|
||||
To define an analysis, a variable of type \mintinline{go}|*analysis.Analyzer| has to be declared:
|
||||
|
||||
\begin{gocode}
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "assigncheck",
|
||||
Doc: "reports reassignments",
|
||||
Run: func(*analysis.Pass) (interface{}, error)
|
||||
}
|
||||
\end{gocode}
|
||||
|
||||
The necessary steps are now adding the `Run' function and registering the analyser
|
||||
in the \mintinline{go}|main()| function.
|
||||
|
||||
The `Run' function takes an \mintinline{go}|*analysis.Pass| type. The Pass provides
|
||||
information about the package that is being analysed and some helper-functions to report
|
||||
diagnostics.
|
||||
|
||||
With `analysis.Pass.Files` and the help of the `go/ast` package, traversing the syntax
|
||||
tree of every file in a package becomes extremely convenient:
|
||||
|
||||
\begin{gocode}
|
||||
for _, file := range pass.Files {
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
// node analysis here
|
||||
})
|
||||
}
|
||||
\end{gocode}
|
||||
|
||||
To implement funcheck as described, five different AST node types need to be
|
||||
taken care of. The simpler ones are
|
||||
\mintinline{go}|*ast.IncDecStmt|, \mintinline{go}|*ast.ForStmt| and \mintinline{go}|*ast.RangeStmt|.
|
||||
An `IncDecStmt' node is a \mintinline{go}|x++| or \mintinline{go}|x--|
|
||||
expression and should always be reported.
|
||||
`ForStmt' and `RangeStmt' are similar; a `RangeStmt' is a `for' loop with the
|
||||
\mintinline{go}|range| keyword instead of an init-, condition- and post-statement.
|
||||
|
||||
Both of these loop-types need to be reported explicitly as they do not show up
|
||||
as reassignments in the AST.
|
||||
Thus, the basic building block for the AST traversal is the following \mintinline{go}|switch|
|
||||
statement:
|
||||
\begin{code}
|
||||
\gofilerange{../work/funcheck/assigncheck/assigncheck.go}{start-basictypes}{end-basictypes}%
|
||||
\caption{Handling the basic AST types in funcheck\autocite{funcheck-ast-types}}
|
||||
\end{code}
|
||||
The remaining two node types are \mintinline{go}|*ast.DeclStmt| and \mintinline{go}|*ast.AssignStmt|.
|
||||
They are not as simple to handle, which is why they are covered in their own chapters.
|
||||
|
||||
\subsection{Detecting reassignments}
|
||||
|
||||
To recapitulate, the goal of this step is to detect all assignments except blank identifiers
|
||||
(discarded values cannot be mutated) and function literals, if the function is declared in the
|
||||
last statement\footnote{This rule is to simplify the logic of the checker and make it easier
|
||||
for developers to read the code. It means that no code may be between \mintinline{go}|var f func|
|
||||
and \mintinline{go}|f = func() { ... }|.}.
|
||||
|
||||
To detect such reassignments, funcheck iterates over all identifiers on the left-hand side
|
||||
of an assignment statement.
|
||||
|
||||
On the left-hand side of an assignment is a list of expressions. These expressions can be
|
||||
identifiers, index expressions (\mintinline{go}|*ast.IndexExpr|, for map and slice access),
|
||||
a `star expression' (\mintinline{go}|*ast.StarExpr|\footnote{star expressions
|
||||
are expressions that are prefixed by an asterisk, dereferencing a pointer. For example
|
||||
\mintinline{go}|*x = 5|, if \mintinline{go}|x| is of type \mintinline{go}|*int|.}) or others.
|
||||
|
||||
If the expression is not an identifier, the assignment must be a reassignment, as all non-identifier
|
||||
expressions contain an already declared identifier. For example, the slice index expression
|
||||
\mintinline{go}|s[5]| is of type \mintinline{go}|*ast.IndexExpr|:
|
||||
\begin{gocode}
|
||||
// An IndexExpr node represents an expression followed by an index.
|
||||
IndexExpr struct {
|
||||
X Expr // expression
|
||||
Lbrack token.Pos // position of "["
|
||||
Index Expr // index expression
|
||||
Rbrack token.Pos // position of "]"
|
||||
}
|
||||
\end{gocode}
|
||||
|
||||
Where \mintinline{go}|IndexExpr.X| is our identifier `s' (of type \mintinline{go}|*ast.Ident|)
|
||||
and a \mintinline{go}|IndexExpr.Index| is \mintinline{go}|5| (of type \mintinline{go}|*ast.BasicLit|).
|
||||
|
||||
As these nested identifiers already need to be declared beforehand (else they could not be used
|
||||
in the expression), all expressions on the left-hand side of an assignment that are not identifiers
|
||||
are reassignments.
|
||||
|
||||
Identifiers are the only expressions that can occur in declarations and reassignments. A naive
|
||||
approach would be to check for the colon in a short variable declaration (\mintinline{go}|:=|).
|
||||
However, as touched upon in Chapter~\ref{sec:multi-assign}, even short variable declarations may
|
||||
contain redeclarations, if at least one variable is new.
|
||||
|
||||
Thus, another approach is needed.
|
||||
|
||||
Every identifier (an AST node with type \mintinline{go}|*ast.Ident|) contains an object\footnote{`An
|
||||
object describes a named language entity such as a package, constant, type, variable,
|
||||
function (incl. methods), or a label'\autocite{go-ast-object}.} that links to the declaration.
|
||||
|
||||
This declaration, of whatever type it may be, always has a position (and a corresponding function
|
||||
to retrieve that position) in the source file.
|
||||
|
||||
A reassignment is detected if an identifier's declaration position does not match the assignment's
|
||||
position (indicating that the variable is being assigned at a different place to where it is
|
||||
declared).
|
||||
|
||||
This is illustrated in the code block~\ref{code:assign-pos}. What can be clearly seen is that in
|
||||
the assignment \mintinline{go}|y = 3|, \mintinline{go}|y|'s declaration refers to the position
|
||||
of the first assignment \mintinline{go}|x, y := 1, 2|, the position where \mintinline{go}|y| has
|
||||
been declared.
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
Assignment "x, y := 1, 2": 2958101
|
||||
Ident "x": 2958101
|
||||
Decl "x, y := 1, 2": 2958101
|
||||
Ident "y": 2958104
|
||||
Decl "x, y := 1, 2": 2958101
|
||||
Assignment "y = 3": 2958115
|
||||
Ident "y": 2958115
|
||||
Decl "x, y := 1, 2": 2958101
|
||||
\end{gocode}
|
||||
\caption{Illustration of an assignment node and corresponding positions\autocite{ast-positions}\label{code:assign-pos}}
|
||||
\end{listing}
|
||||
|
||||
As this technique works on an identifier level, multi-variable declarations or assignments
|
||||
can be verified without any additional effort.
|
||||
|
||||
If a variable in a short variable declaration is being reassigned, the variable's `Declaration'
|
||||
field will point to the original position of its declaration, which can be easily detected
|
||||
(as shown in code block~\ref{code:assign-pos}).
|
||||
|
||||
\subsection{Handling function declarations}
|
||||
|
||||
In contrast to all other variable types, function variables may be `reassigned' once.
|
||||
As discussed in Chapter~\ref{sec:func-reassign}, this is to allow recursive function
|
||||
literals. Detecting and not reporting these assignments is a two-step process, as two
|
||||
consecutive AST nodes need to be inspected.
|
||||
|
||||
The first step is to detect function declarations; statements of the form
|
||||
\mintinline{go}|var f func() |. Should such a statement be encountered,
|
||||
its position is saved for the following AST node.
|
||||
|
||||
In the consecutive AST node it is ensured that, if the node is an assignment and
|
||||
the assignee identifier is of type function literal, the position matches the
|
||||
previously saved one.
|
||||
|
||||
The position of the declaration and AST node structure can be seen in~\ref{code:func-reassign}
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
Declaration "var f func() int": 2958142
|
||||
Ident "f func() int": 2958146
|
||||
Assignment "f = func() int { return y }": 2958160
|
||||
Ident "f": 2958160
|
||||
Decl "f func() int": 2958146
|
||||
\end{gocode}
|
||||
\caption{Illustration of a function literal assignment\autocite{ast-positions}\label{code:func-reassign}}
|
||||
\end{listing}
|
||||
|
||||
With this technique it is possible to exempt functions from the reassignment rule.
|
||||
|
||||
\subsection{Testing Funcheck}
|
||||
|
||||
The analysis-package is distributed with a sub-package `analysistest'. This package makes
|
||||
it extremely simple to test a code analysis tool.
|
||||
|
||||
By providing test data and the expected messages from funcheck in a structured way, all
|
||||
that is needed to test funcheck is:
|
||||
|
||||
\begin{code}
|
||||
\begin{gocode}
|
||||
package assigncheck
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
)
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
analysistest.Run(t, analysistest.TestData(), Analyzer)
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Testing a code analyser with the `analysistest' package}
|
||||
\end{code}
|
||||
|
||||
The library expects the test data in the current directory in a folder named `testdata' and
|
||||
then spawns and executes the analyser on the files in that folder. Comments in those files
|
||||
are used to describe the expected message:
|
||||
|
||||
\begin{gocode}
|
||||
x := 5
|
||||
fmt.Println(x)
|
||||
x = 6 // want `^reassignment of x$`
|
||||
fmt.Println(x)
|
||||
\end{gocode}
|
||||
|
||||
This will ensure that on the line \mintinline{go}|x = 6| an error message is reported that says
|
||||
`reassignment of x'.
|
@ -0,0 +1,300 @@
|
||||
% -*- mode: latex; coding: utf-8; TeX-master: ../thesis -*-
|
||||
% !TEX TS-program = pdflatexmk
|
||||
% !TEX encoding = UTF-8 Unicode
|
||||
% !TEX root = ../thesis.tex
|
||||
|
||||
\section{Demonstration of the new built-in functions}
|
||||
|
||||
The new built-in functions can be used in the same manner as the existing built-ins like
|
||||
\mintinline{go}|append|. This example serves as a demonstration by modifying a slice
|
||||
of integers, although they can be used to modify a slice of any type.
|
||||
\begin{code}
|
||||
\begin{gocode}
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Counterpart to Haskell's `derive Show` through code generation
|
||||
//go:generate stringer -type parity
|
||||
|
||||
type parity int
|
||||
|
||||
const even parity = 0
|
||||
const odd parity = 1
|
||||
|
||||
// shouldBe returns a function that returns true if an int is of the
|
||||
// given parity
|
||||
func shouldBe(p parity) func(i int) bool {
|
||||
return func(i int) bool {
|
||||
return i%2 == int(p)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
lst := []int{1, 2, 3, 4, 5}
|
||||
lstMult := fmap(func(i int) int { return i * 5 }, prepend(0, lst))
|
||||
|
||||
addToString := func(s string, i int) string {
|
||||
return s + strconv.Itoa(i) + " "
|
||||
}
|
||||
// fold over even / odd numbers and add them to a string
|
||||
evens := foldl(addToString, even.String()+": ",
|
||||
filter(shouldBe(even), lstMult))
|
||||
odds := foldl(addToString, odd.String()+": ",
|
||||
filter(shouldBe(odd), lstMult))
|
||||
|
||||
fmt.Println(evens, odds) // even: 0 10 20 odd: 5 15 25
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Demonstration of the new built-in functions\label{code:funcexample}}
|
||||
\end{code}
|
||||
|
||||
\section{Refactoring the Prettyprint Package}
|
||||
|
||||
\newglossaryentry{stdout}{name=stdout, description={Standard Output, the default
|
||||
output stream for programs}}
|
||||
|
||||
The code blocks~\ref{code:assign-pos} and~\ref{code:func-reassign} have been
|
||||
generated by a small package `prettyprint' contained in the funcheck repository.
|
||||
|
||||
To see how the newly built-in functions and funcheck can be used, this `prettyprint' package
|
||||
can be refactored to a purely functional implementation.
|
||||
The current version of the package is written in what could be considered idiomatic
|
||||
Go\footnote{
|
||||
There is no exact definition of what idiomatic Go is, so this interpretation
|
||||
could be challenged. It is idiomatic Go code to the author.
|
||||
}.
|
||||
|
||||
|
||||
The prettyprinter is based on the same framework as assigncheck\footnote{Assigncheck
|
||||
is the main package for funcheck and checks the reassignments}, but instead
|
||||
of reporting anything, it prints AST information to \gls{stdout}.
|
||||
|
||||
Similarly to assigncheck, the main logic of the package is within a
|
||||
function literal that is being passed to the \mintinline{go}|ast.Inspect|
|
||||
function.
|
||||
|
||||
Prettyprint only checks two AST node types, \mintinline{go}|*ast.DeclStmt|
|
||||
and \mintinline{go}|*ast.AssignStmt| (declarations and assignments).
|
||||
|
||||
For example, for the program
|
||||
\begin{gocode}
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
x, y := 1, 2
|
||||
y = 3
|
||||
fmt.Println(x, y)
|
||||
}
|
||||
\end{gocode}
|
||||
the following AST information is printed:
|
||||
|
||||
\begin{gocode}
|
||||
Assignment "x, y := 1, 2": 2958101
|
||||
Ident "x": 2958101
|
||||
Decl "x, y := 1, 2": 2958101
|
||||
Ident "y": 2958104
|
||||
Decl "x, y := 1, 2": 2958101
|
||||
Assignment "y = 3": 2958115
|
||||
Ident "y": 2958115
|
||||
Decl "x, y := 1, 2": 2958101
|
||||
\end{gocode}
|
||||
|
||||
To refactor it to a purely functional version, funcheck can be used to
|
||||
list reassignments:
|
||||
|
||||
\begin{bashcode}
|
||||
$> funcheck .
|
||||
prettyprint.go:20:2: internal reassignment (for loop) in "for _, file := range pass.Files { ... }"
|
||||
prettyprint.go:42:2: internal reassignment (for loop) in "for i := range decl.Specs { ... }"
|
||||
prettyprint.go:67:2: internal reassignment (for loop) in "for _, expr := range as.Lhs { ... }"
|
||||
\end{bashcode}
|
||||
As can be seen in the output, the package uses 3 \mintinline{go}|for| loops to range over
|
||||
slices. However, there are no other reassignments of variables in the code.
|
||||
|
||||
The code to print declarations, which causes the second lint message, is as shown in code block~\ref{code:decl-printing}.
|
||||
|
||||
\begin{code}
|
||||
\begin{gocode}
|
||||
func checkDecl(as *ast.DeclStmt, fset *token.FileSet) {
|
||||
fmt.Printf("Declaration %q: %v\n", render(fset, as), as.Pos())
|
||||
decl, ok := as.Decl.(*ast.GenDecl)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for i := range decl.Specs {
|
||||
val, ok := decl.Specs[i].(*ast.ValueSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if val.Values != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := val.Type.(*ast.FuncType); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("\tIdent %q: %v\n", render(fset, val), val.Names[0].Pos())
|
||||
}
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Pretty-printing declarations in idiomatic Go\label{code:decl-printing}}
|
||||
\end{code}
|
||||
To convert this for-loop appropriately, the new built-in `foldl' can be used.
|
||||
To recapitulate, the `foldl' function is being defined as:
|
||||
\begin{gocode}
|
||||
func foldl(fn func(Type1, Type) Type1, acc Type1, slice []Type) Type1
|
||||
\end{gocode}
|
||||
As `foldl' requires a return type, a dummy type `null" can be introduced, which
|
||||
is just an empty struct:
|
||||
\begin{gocode}
|
||||
type null struct{}
|
||||
\end{gocode}
|
||||
Now the code within the for loop can be used to create a function literal:
|
||||
\begin{gocode}
|
||||
check := func(_ null, spec ast.Spec) (n null) {
|
||||
// implementation
|
||||
}
|
||||
\end{gocode}
|
||||
There are two subtleties in regards to the introduced null type:
|
||||
First, the null value that is being passed as an argument is being discarded
|
||||
by the use of an empty identifier.
|
||||
Secondly, the return value is `named', which means the variable `n' is
|
||||
already declared in the function block. Because of this, `naked returns' can
|
||||
be used, so there is no need to specify which variable is being returned.
|
||||
|
||||
The code snippet~\ref{code:decl-printing} can be translated to:
|
||||
|
||||
\begin{code}
|
||||
\begin{gocode}
|
||||
func checkDecl(as *ast.DeclStmt, fset *token.FileSet) {
|
||||
fmt.Printf("Declaration %q: %v\n", render(fset, as), as.Pos())
|
||||
|
||||
check := func(_ null, spec ast.Spec) (n null) {
|
||||
val, ok := spec.(*ast.ValueSpec)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if val.Values != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := val.Type.(*ast.FuncType); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\tIdent %q: %v\n", render(fset, val), val.Names[0].Pos())
|
||||
return
|
||||
}
|
||||
|
||||
if decl, ok := as.Decl.(*ast.GenDecl); ok {
|
||||
_ = foldl(check, null{}, decl.Specs)
|
||||
}
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{Pretty-printing declarations in functional Go}
|
||||
\end{code}
|
||||
The for-loop has been replaced by a `foldl', where a function closure
|
||||
that contains the actual processing is passed.
|
||||
|
||||
While this still looks similar to the original example, this is mostly due to
|
||||
the `if' statements. In Haskell, pattern matching would be used and nil checks
|
||||
could be omitted entirely. Also, as Haskell's type system is more advanced, the
|
||||
handling of those types would be different too.
|
||||
|
||||
However, the goal of this thesis is to make functional code look more familiar
|
||||
to programmers that are used to imperative code.
|
||||
And while it may not look like it, the code does not use any mutation of
|
||||
variables\footnote{Libraries may do, but the scope is not to rewrite any existing
|
||||
libraries.}, for loops or global state. Therefore, it can be concluded that this
|
||||
snippet is purely functional as per the definition from Chapter~\ref{sec:func-purity}.
|
||||
|
||||
\section{Quicksort}
|
||||
|
||||
In Chapter~\ref{code:haskell-quicksort}, a naive implementation of the Quicksort sorting
|
||||
algorithm has been introduced.
|
||||
Implementing this algorithm in Go is now straightforward and the similarities between
|
||||
the Haskell implementation and the functional Go implementation are striking:
|
||||
|
||||
\begin{listing}
|
||||
\begin{gocode}
|
||||
func quicksort(p []int) []int {
|
||||
if len(p) == 0 {
|
||||
return []int{}
|
||||
}
|
||||
|
||||
lesser := filter(func(x int) bool { return p[0] > x }, p[1:])
|
||||
greater := filter(func(x int) bool { return p[0] <= x }, p[1:])
|
||||
|
||||
return append(quicksort(lesser), prepend(p[0], quicksort(greater))...)
|
||||
}
|
||||
\end{gocode}
|
||||
\begin{haskellcode}
|
||||
quicksort :: Ord a => [a] -> [a]
|
||||
quicksort [] = []
|
||||
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
|
||||
where
|
||||
lesser = filter (< p) xs
|
||||
greater = filter (>= p) xs
|
||||
\end{haskellcode}
|
||||
\caption{Quicksort implementations compared}
|
||||
\end{listing}
|
||||
|
||||
Again, the Go implementation bridges the gap between being imperative and functional,
|
||||
while still being obvious about the algorithm.
|
||||
Furthermore, as expected, when inspecting the code with funcheck, no non-functional
|
||||
constructs are reported.
|
||||
|
||||
\section{Comparison to Java Streams}
|
||||
|
||||
In Java 8, concepts from functional programming have been introduced to the language.
|
||||
The major new feature was Lambda Expressions --- anonymous function literals --- and
|
||||
streams. Streams are an abstract layer to process data in a functional way, with `map',
|
||||
`filter', `reduce' and more.
|
||||
|
||||
Java Streams are similar to the new built-in functions in this thesis:
|
||||
|
||||
\begin{listing}
|
||||
\begin{javacode}
|
||||
List<Integer> even = list.stream()
|
||||
.filter(x -> x % 2 == 0)
|
||||
.collect(Collectors.toList());
|
||||
\end{javacode}
|
||||
\begin{gocode}
|
||||
even := filter(
|
||||
func(x int) bool { return x%2 == 0 },
|
||||
list)
|
||||
\end{gocode}
|
||||
\caption{Comparison Java Streams and functional Go}
|
||||
\end{listing}
|
||||
|
||||
The lambda-syntax in Java is more concise than Go's function literals, where the
|
||||
complete function header has to be provided\footnote{There is an open proposal
|
||||
to add a lightweight anonymous function syntax to Go 2, which, if implemented,
|
||||
would resolve this verbosity\autocite{go-lambdas}}.
|
||||
|
||||
However, the conversion to a stream and back to a list (with \mintinline{java}|list.stream()| and
|
||||
\mintinline{java}|.collect(Collectors.toList())|)
|
||||
is not required in Go, reducing the mental overhead for the programmer.
|
||||
|
||||
Apart from syntactical differences, Java Streams contain all the functions that
|
||||
have been added as built-ins to Go, and a lot more.
|
||||
|
||||
On the other hand, Java's Syntax is arguably more complex than Go. An indicator for this might be
|
||||
the language specification; Go's Language Specification is roughly 110 pages, while
|
||||
Java's specification spans more than 700 pages\footnote{
|
||||
The Java 8 Specification is 724\autocite{java-8-spec}, the Java 14
|
||||
Specification 774\autocite{java-14-spec} pages.}, more than 6 times the size.
|
||||
|
||||
The consideration of which language to choose comes down to the experience with either language.
|
||||
An experienced Java programmer will find it easier to start with Java's toolset, while programmers
|
||||
coming from a C background may choose Go.
|
@ -0,0 +1,122 @@
|
||||
% -*- mode: latex; coding: utf-8; TeX-master: ../thesis -*-
|
||||
% !TEX TS-program = pdflatexmk
|
||||
% !TEX encoding = UTF-8 Unicode
|
||||
% !TEX root = ../thesis.tex
|
||||
|
||||
To learn functional programming without being introduced to a new syntax at the same time
|
||||
ensures that programmers can fully concentrate on functional concepts. Although Go already
|
||||
supported a functional programming style, the programmer may not have known if the code was
|
||||
purely functional or if there still were imperative constructs embedded.
|
||||
|
||||
In the last chapters, functional purity has been defined as a law based on one simple rule: immutability.
|
||||
Immutability dictates that once assigned, a variable's value never changes.
|
||||
This in turn leads to function purity, which means that functions do not have side
|
||||
effects and their return value is solely influenced by the function's parameters.
|
||||
|
||||
It has been shown that although purely functional languages like Haskell aim to be completely
|
||||
pure, this objective is difficult to accomplish. The reason for this are Input / Output actions; user
|
||||
input, network connections, randomness and time are all impure. Haskell wraps
|
||||
these impure functions in the IO monad, which is a way to work around the compiler's optimisations
|
||||
based on functional purity. In addition, although the IO monad does not make impure functions pure, it
|
||||
does serve as documentation to its users (`if the function has IO, it is impure') and
|
||||
guarantees a certain execution order.
|
||||
|
||||
Go on the other hand does not have this issue, as the Go compiler does not optimise execution
|
||||
based on purity guarantees. Having a similar construct to the IO monad in Go would, as such,
|
||||
only serve documentation purposes. Because of this, the decision has been taken to ignore
|
||||
the impurity that is implied with IO actions.
|
||||
|
||||
Apart from IO, to achieve functional purity, the global state of a program should not influence
|
||||
the return values of specific function. This ties into immutablitiy; if global state can
|
||||
not be mutated, it can also not influence or change the result value of a function.
|
||||
|
||||
Based on these observations, a static code analysis tool has been developed that reports
|
||||
all reassignments of variables. In other words, it forbids the usage of the regular
|
||||
assignment operator (\mintinline{go}|=|), only allowing the short variable declarations
|
||||
(\mintinline{go}|:=|). However, the experienced Go developer may know that the \mintinline{go}|:=|
|
||||
operator can also reassign previously declared variables, implying that the solution to the
|
||||
problem is not as simple as forbidding the assignment operator.
|
||||
Further, there are many more edge cases that have been detected with careful testing:
|
||||
To recursively call function literals, they must be declared beforehand (before assigning
|
||||
the actual function to it) because of Go's scoping rules. Additionally, exceptions
|
||||
had to be made for the blank identifier (\mintinline{go}|_|) and variables that are declared
|
||||
outside of the current file.
|
||||
|
||||
With all of this in place, an algorithm has been chosen that is based on the identifier's
|
||||
declaration position. In the \gls{ast} that is being checked, every identifier node has a field
|
||||
which contains the position of it's declaration. If this does not match the current identifier's
|
||||
position, the operation must be a reassignment.
|
||||
The resulting binary, called `funcheck', successfully reports such reassignments:
|
||||
|
||||
\begin{gocode}
|
||||
s := "hello world"
|
||||
fmt.Println(s)
|
||||
s = "and goodbye"
|
||||
fmt.Println(s)
|
||||
\end{gocode}
|
||||
|
||||
\begin{bashcode}
|
||||
$> funcheck .
|
||||
file.go:3:2: reassignment of s
|
||||
\end{bashcode}
|
||||
|
||||
This linter can be used and executed against any Go package. To eliminate the reported errors,
|
||||
code has to be rewritten in a purely functional manner.
|
||||
|
||||
However, functional code often relies heavily on lists and list-processing functions.
|
||||
Although Go does not have a built-in list datatype, Go's slices, an abstraction built
|
||||
on top of arrays,
|
||||
mitigate a lot of downsides when comparing regular arrays to lists\footnote{Arrays / Slices
|
||||
and Lists have a different runtime behaviour (indexing, adding or removing elements).
|
||||
However, the performance of the code was not considered to be in scope for this thesis.}.
|
||||
|
||||
What Go's slices lack on the other hand are the typical higher-order functions like `map',
|
||||
`filter' or `reduce'. These are commonly used in functional programming and most languages
|
||||
contain implementations of these functions already --- Go does not.
|
||||
|
||||
Due to the lack of polymorphism, writing implementations for these functions
|
||||
would result in a lot of duplicated code. To mitigate this issue, the most
|
||||
common higher-order functions have been added to the list of Go's built-in functions,
|
||||
which are registered, type-checked and implemented within the Go compiler.
|
||||
As these are handled directly at compile time, built-in functions may be polymorphic, for
|
||||
example allowing the programmer to use the same `filter' function for all list-types.
|
||||
|
||||
To determine which higher-order functions are most commonly used, the most popular
|
||||
open-source Haskell projects (pandoc, shellcheck and purescript, to name a few) have
|
||||
been analysed. As a result, `fmap', `fold', `filter' and `prepend'
|
||||
(`cons') have been added as built-ins into the compiler.
|
||||
These functions make it easier to write purely functional code in Go, in turn helping
|
||||
the programmer to learn functional programming with a familiar language and syntax.
|
||||
|
||||
While implementing these functions in a regular Go program would be a matter of minutes,
|
||||
adding them to the Go compiler is not as simple. To illustrate, the functions
|
||||
have been written out in regular Go in the chapters~\ref{ch:impl-fmap} to~\ref{ch:impl-filter}
|
||||
and are 33 lines of code, all functions combined. In the Go compiler, it is necessary to
|
||||
register the functions, type-check the calls and manipulate the \gls{ast} instead of writing
|
||||
the algorithm in Go code directly. This took more than 800 lines of code to do so.
|
||||
|
||||
As a result, using these functions is equal to using any other built-in function: there
|
||||
is documentation in Godoc, type-checking support in the language server\footnote{If the
|
||||
language server (gopls) is compiled against the modified version of Go, as
|
||||
described in Appendix~\ref{appendix:build-gopls}}
|
||||
and in the compilation phase, as well as a polymorphic function header, allowing the
|
||||
programmer to call the function with any allowed type.
|
||||
|
||||
Demonstrations of these functions and how functional Go code looks like can be seen in
|
||||
Chapter~\ref{ch:application}.
|
||||
|
||||
With these additions to Go and its ecosystem, aspiring functional programmers
|
||||
can fully concentrate of the concepts of functional programming while keeping
|
||||
a familiar syntax at hand. However, it should not be considered a fully featured
|
||||
functional programming language. Rather, it should serve as a starting point and
|
||||
make the transition to a language like Haskell easier.
|
||||
|
||||
Differences in the syntax between Haskell and Go exemplify why purely functional programming
|
||||
languages have a distinct syntax compared to imperative or object-oriented languages.
|
||||
Many constructs can be expressed more concisely in Haskell, without the additional
|
||||
overhead that many programming languages, including Go, introduce.
|
||||
|
||||
Using `the right tool for the job' is important, and this paper shows that imperative or
|
||||
object-oriented programming languages are not the right tool for production-grade
|
||||
functional programming. However, they can serve as a good starting point and help transitioning
|
||||
to a pure functional programming language.
|
@ -0,0 +1,85 @@
|
||||
% -*- mode: latex; coding: utf-8; TeX-master: ../thesis -*-
|
||||
% !TEX TS-program = pdflatexmk
|
||||
% !TEX encoding = UTF-8 Unicode
|
||||
% !TEX root = ../thesis.tex
|
||||
|
||||
The aforementioned extensions to the Go language and its tooling should be a help to learn
|
||||
functional programming. I believe that through these extensions it is easier to write
|
||||
purely functional code in Go, enabling a developer to learn functional programming with
|
||||
a familiar syntax in an obvious way. Here, Go's simplicity and verbosity are a key differentiator
|
||||
to other languages. Instead of having as many features as possible to support every usecase,
|
||||
Go has been designed with simplicity in mind\footnote{For example, Go only has 25 keywords, compared
|
||||
to 37 in C99 and 84 in C++11}.
|
||||
|
||||
In many cases, this leads to more `verbose' code --- more lines of code compared to a similar
|
||||
implementation in other languages. However, I argue that, especially for the first steps
|
||||
in functional programming,
|
||||
|
||||
\begin{quote}
|
||||
Clear is better than clever\autocite{cheney-clear}
|
||||
\end{quote}
|
||||
|
||||
Staying in touch with this core Go principle, this results in functional code that may be
|
||||
verbose, but easy to read and understand.
|
||||
|
||||
It should be clear that the result is not a `production-ready' functional programming language.
|
||||
It is a language to help getting started with functional programming; either by re-implemeting pieces
|
||||
of code that have not been clear in how they work, or by taking an imperative block of code
|
||||
and refactoring it to make it purely functional.
|
||||
|
||||
In many cases, the resulting code will still look familiar to the imperative counterpart,
|
||||
even if `funcheck' assures that it is purely functional. This, I believe, bridges the gap
|
||||
that developers usually have to overcome by themselves.
|
||||
|
||||
To be a purely functional programming language, Go is missing too many features that would be
|
||||
required to write concise functional code. The very basic type system\footnote{Not only
|
||||
does Go not have polymorphism (yet), Go's type system is simple by design: there are no
|
||||
implicit type conversions, no sum types (tagged unions, variant) and almost no type inference.
|
||||
}, no advanced pattern matching and only explicit currying are all examples why Go is not useful
|
||||
in day-to-day functional programming.
|
||||
|
||||
At the same time, the obvious nature of Go is exactly because it is missing all
|
||||
of these features. The Go team explicitly tries not to include too many features within
|
||||
the language in order to keep the complexity of code to a minimum\autocite{go-feature}.
|
||||
The simplicity of the language is a key feature of Go and an important reason why it was
|
||||
chosen to implement the ideas in the first place.
|
||||
Especially for learning new concepts, hiding implementations and ideas behind features
|
||||
may not be what is desired and helpful.
|
||||
|
||||
On another note, what has not been an aspect in this thesis is
|
||||
performance. Go by itself is relatively performant, however functional constructs, for example
|
||||
recursive function calls, come with a performance cost. While in purely functional languages
|
||||
this can be optimised, Go cannot or does not want to do these optimisations\footnote{For example,
|
||||
with tail call optimisation, the Go team explicitly decided not to do it because the stack trace
|
||||
would be lost\autocite{go-tco-nope}}.
|
||||
|
||||
While the newly built-in functions do not have any low-hanging fruits in regards to optimisation,
|
||||
they would benefit from more aggressive inlining\autocite{go-compiler-inline}, for example.
|
||||
Benchmarking and optimising these functions was out of scope for this thesis, but may be tackled
|
||||
in the future.
|
||||
|
||||
\newglossaryentry{sumtypes}{name=sum types,description={Sum types, often also called aggregated types,
|
||||
variant or tagged union is a data structure that can hold one of several, predefined data types. For
|
||||
example, Haskell's \mintinline{haskell}|Either| holds either a value of type A or type B. Similar to that,
|
||||
\mintinline{haskell}|Maybe| can hold either a concrete value, or `Nothing'}}
|
||||
|
||||
The number one issue that still exists is the simple type system. Not only the lack of polymorphism, which
|
||||
has been mitigated slightly by providing the most used higher-order functions as built-ins, but also the
|
||||
lack of algebraic data types, especially \gls{sumtypes}.
|
||||
|
||||
Algebraic data types can be split up into two groups, product types and sum types.
|
||||
Most product types can be built with Go too; records are basically
|
||||
equal to structs, and tuples are not needed too often, as functions can just return multiple values.
|
||||
Sum types however are not available in Go at all. It is possible to imitate sum types in Go
|
||||
with interfaces (see the example code in Appendix~\ref{appendix:sum-types}), and ensure an
|
||||
exhaustive match by a linter. This linter should have been implemented as part of this thesis.
|
||||
However, after careful consideration, it has been decided that this will not be done
|
||||
due to the simple reason that such a linter already exists\autocite{sushi-sumtypes}.
|
||||
That linter could be merged with `funcheck', which is left as an exercise to the reader.
|
||||
|
||||
Further, a linter does not eliminate the fact that sum types are not properly integrated
|
||||
into the language.
|
||||
This may be an interesting area for further
|
||||
research and implementation possibilities\footnote{There is a mention about sum types in the current
|
||||
Go Generics Proposal\autocite{go-generics-proposal}, but the initial implementation of generics will
|
||||
most likely not contain sum types}.
|
@ -0,0 +1,424 @@
|
||||
\section{Information about this thesis}
|
||||
|
||||
This thesis, including this document, is contained in a single git repository
|
||||
that can be found at GitHub\autocite{git-repo}.
|
||||
|
||||
All the work that has been done is open sourced under the Apache License 2.0,
|
||||
except the Go source code, which has its own license.
|
||||
|
||||
To view the code, build or reproduce results, this git repository can be cloned
|
||||
and its submodules checked out. The version that has been submitted can be checked
|
||||
out through the tag `v1.0.0':
|
||||
\begin{bashcode}
|
||||
$> git clone https://github.com/tommyknows/bachelor-thesis.git
|
||||
$> cd bachelor-thesis
|
||||
$> git checkout v1.0.0
|
||||
$> git submodule init
|
||||
$> git submodule update
|
||||
\end{bashcode}
|
||||
|
||||
The directory `thesis' contains the \LaTeX\ code for this paper. There is a helper
|
||||
required for displaying code in the thesis which is located in `thesis/utils' and
|
||||
needs to be built with `go build .'. However, a PDF which should be on the same state
|
||||
as the source is provided in the repository too (`thesis/thesis.pdf').
|
||||
|
||||
The `work' directory contains code that has been developed in this thesis. In this
|
||||
directory, the Go and funcheck source code can be found (see Appendix~\ref{appendix:install-fgo}
|
||||
and~\ref{appendix:build-funcheck}). Further, some examples
|
||||
of functional Go have been developed in the `example' directory. The `common-list-functions'
|
||||
folder contains the script as mentioned in Appendix~\ref{appendix:function-occurrences}.
|
||||
|
||||
\section{Example for Functional Options}\label{appendix:funcopts}
|
||||
|
||||
This source code example demonstrates `functional options' as introduced in this paper.
|
||||
Functional options are usually passed to a constructor to configure the new instance, in
|
||||
this case a web server. The advantages of functional options are that the API ends up to
|
||||
be cleaner and more easily extensible and allows the default use-case to be as simple
|
||||
as possible.
|
||||
|
||||
\begin{code}
|
||||
\gofile{../work/examples/functional-options/main.go}%
|
||||
\caption{Functional Options for a simple web server}
|
||||
\end{code}
|
||||
|
||||
\section{Analysis of function occurrences in Haskell code}\label{appendix:function-occurrences}
|
||||
The results of the analysis have been acquired by running the `count-function' script
|
||||
that is located in `work/common-list-functions' in the git repository\autocite{git-repo}.
|
||||
|
||||
The script utilises \href{https://github.com/BurntSushi/ripgrep}{ripgrep} to count the number of occurrences, so
|
||||
this must be installed in order to run this script.
|
||||
|
||||
\begin{bashcode}
|
||||
./count-function.sh ":" "((map)|(fmap))" "((foldr)|(foldl'?))" "filter" "reverse" "take" "drop" "sum" "zip" "product" "maximum" "min
|
||||
imum"
|
||||
Searching for occurrences in subdirectories of ./common-list-functions
|
||||
Found 2912 occurrences of ":"
|
||||
Found 1873 occurrences of "((map)|(fmap))"
|
||||
Found 303 occurrences of "((foldr)|(foldl'?))"
|
||||
Found 262 occurrences of "filter"
|
||||
Found 154 occurrences of "reverse"
|
||||
Found 108 occurrences of "take"
|
||||
Found 81 occurrences of "drop"
|
||||
Found 44 occurrences of "sum"
|
||||
Found 38 occurrences of "zip"
|
||||
Found 15 occurrences of "product"
|
||||
Found 45 occurrences of "maximum"
|
||||
Found 10 occurrences of "minimum"
|
||||
\end{bashcode}
|
||||
|
||||
The terms are searched with a leading and trailing space to get exact matches. Further, as can be seen in
|
||||
the call to the script, the search combines the results of map together with fmap, and foldr with foldl
|
||||
and foldl'.
|
||||
|
||||
\section{Mutating variables in Go}\label{appendix:mutation}
|
||||
|
||||
Source Code~\ref{code:mutation} shows how pointers are used to mutate data in Go. This does
|
||||
not necessarily need to be a `struct' type, pointers can be used of any type.
|
||||
Pointers have to be used to modify values because Go is a copy-by-value language and thus
|
||||
copies the parameters that are passed to a function.
|
||||
|
||||
\begin{code}
|
||||
\gofile{../work/examples/mutate/main.go}%
|
||||
\caption{Example on how to mutate data in Go}\label{code:mutation}
|
||||
\end{code}
|
||||
|
||||
\section{Shadowing variables in Go}\label{appendix:shadowing}
|
||||
|
||||
Source Code~\ref{code:shadowing} demonstrates the block scoping and shadowing rules
|
||||
in Go.
|
||||
|
||||
\begin{code}
|
||||
\gofile{../work/examples/shadowing/main.go}%
|
||||
\caption{Example on how shadowing works on block scopes}\label{code:shadowing}
|
||||
\end{code}
|
||||
|
||||
\section{Foldl and Foldl' difference}\label{appendix:foldl-strictness}
|
||||
|
||||
This code example shows the difference between foldl and foldl' in their
|
||||
execution. What can be seen is that foldl builds up a call stack, while
|
||||
foldl' executes the calls during the traversal.
|
||||
|
||||
\begin{code}
|
||||
\begin{haskellcode}
|
||||
> (?) :: Int -> Int -> Int
|
||||
> _ ? 0 = 0
|
||||
> x ? y = x*y
|
||||
>
|
||||
> list :: [Int]
|
||||
> list = [2,3,undefined,5,0]
|
||||
>
|
||||
> foldl (?) 1 list
|
||||
foldl (?) 1 [2,3,undefined,5,0] -->
|
||||
foldl (?) (1 ? 2) [3,undefined,5,0] -->
|
||||
foldl (?) ((1 ? 2) ? 3) [undefined,5,0] -->
|
||||
foldl (?) (((1 ? 2) ? 3) ? undefined) [5,0] -->
|
||||
foldl (?) ((((1 ? 2) ? 3) ? undefined) ? 5) [0] -->
|
||||
foldl (?) (((((1 ? 2) ? 3) ? undefined) ? 5) ? 0) [] -->
|
||||
((((1 ? 2) ? 3) ? undefined) ? 5) ? 0 -->
|
||||
0
|
||||
|
||||
> foldl' (?) 1 list
|
||||
foldl' (?) 1 [2,3,undefined,5,0] -->
|
||||
1 ? 2 --> 2
|
||||
foldl' (?) 2 [3,undefined,5,0] -->
|
||||
2 ? 3 --> 6
|
||||
foldl' (?) 6 [undefined,5,0] -->
|
||||
6 ? undefined -->
|
||||
*** Exception: Prelude.undefined
|
||||
\end{haskellcode}
|
||||
\caption{foldl and foldl' strictness\autocite{fold-types}}
|
||||
\end{code}
|
||||
|
||||
\section{Workaround for the missing foldl implementation in Go}\label{appendix:foldl-go}
|
||||
|
||||
This code block exemplifies how foldl could be implemented in Go code. It is based on the example
|
||||
from Appendix~\ref{appendix:foldl-strictness}, rewritten in Go. While Go does not know about
|
||||
the concept of laziness, the programmer may implement the laziness himself by working with function
|
||||
closures.
|
||||
|
||||
In this example, \mintinline{go}|*int| is used instead of \mintinline{go}|int| to simulate Haskell's
|
||||
\mintinline{haskell}|undefined| with a nil-pointer.
|
||||
If a nil-pointer is dereferenced, the program will panic.
|
||||
|
||||
In the lazy version of this code (utilising `myFold' and `mulLazy'), the panic does not occur because
|
||||
the nil-pointer is never dereferenced as the function closure is never executed. This is equal
|
||||
to the `foldl' demonstration in Appendix~\ref{appendix:foldl-strictness}.
|
||||
|
||||
The non-lazy version (`foldl' and `mul') executes the function while traversing the slice and thus panics.
|
||||
|
||||
\begin{code}
|
||||
\gofile{../work/examples/foldl-workaround/main.go}%
|
||||
\begin{bashcode}
|
||||
$> fgo run .
|
||||
0
|
||||
panic: runtime error: invalid memory address or nil pointer dereference
|
||||
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x109e945]
|
||||
|
||||
goroutine 1 [running]:
|
||||
...
|
||||
\end{bashcode}
|
||||
\caption{Working around the missing foldl implementation in Go\label{code:foldl-go}}
|
||||
\end{code}
|
||||
|
||||
\section{Prettyprint implementation}\label{appendix:prettyprint-func}
|
||||
|
||||
These code blocks show the same package `prettyprint', once in idiomatic Go and once
|
||||
in functional Go. What can be seen is that the \mintinline{go}|for| loops have been replaced
|
||||
by the usage of `foldl' and anonymous functions.
|
||||
|
||||
\begin{code}
|
||||
\gofile{../work/funcheck/prettyprint/prettyprint.go}%
|
||||
\caption{The original prettyprint implementation\autocite{prettyprint-orig}}
|
||||
\end{code}
|
||||
\begin{code}
|
||||
\begin{gocode}
|
||||
package prettyprint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "prettyprint",
|
||||
Doc: "prints positions",
|
||||
Run: run,
|
||||
}
|
||||
|
||||
type null struct{}
|
||||
|
||||
func checkDecl(as *ast.DeclStmt, fset *token.FileSet) {
|
||||
fmt.Printf("Declaration %q: %v\n", render(fset, as), as.Pos())
|
||||
|
||||
check := func(_ null, spec ast.Spec) (n null) {
|
||||
val, ok := spec.(*ast.ValueSpec)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if val.Values != nil {
|
||||
return
|
||||
}
|
||||
if _, ok := val.Type.(*ast.FuncType); !ok {
|
||||
return
|
||||
}
|
||||
fmt.Printf("\tIdent %q: %v\n", render(fset, val), val.Names[0].Pos())
|
||||
return
|
||||
}
|
||||
|
||||
if decl, ok := as.Decl.(*ast.GenDecl); ok {
|
||||
_ = foldl(check, null{}, decl.Specs)
|
||||
}
|
||||
}
|
||||
|
||||
func checkAssign(as *ast.AssignStmt, fset *token.FileSet) {
|
||||
fmt.Printf("Assignment %q: %v\n", render(fset, as), as.Pos())
|
||||
|
||||
check := func(_ null, expr ast.Expr) (n null) {
|
||||
ident, ok := expr.(*ast.Ident) // Lhs always is an "IdentifierList"
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\tIdent %q: %v\n", ident.String(), ident.Pos())
|
||||
|
||||
switch {
|
||||
case ident.Name == "_":
|
||||
fmt.Printf("\t\tBlank Identifier!\n")
|
||||
case ident.Obj == nil:
|
||||
fmt.Printf("\t\tDecl is not in the same file!\n")
|
||||
default:
|
||||
// make sure the declaration has a Pos func and get it
|
||||
declPos := ident.Obj.Decl.(ast.Node).Pos()
|
||||
fmt.Printf("\t\tDecl %q: %v\n", render(fset, ident.Obj.Decl), declPos)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
_ = foldl(check, null{}, as.Lhs)
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := func(_ null, file *ast.File) (n null) {
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
switch as := n.(type) {
|
||||
case *ast.DeclStmt:
|
||||
checkDecl(as, pass.Fset)
|
||||
case *ast.AssignStmt:
|
||||
checkAssign(as, pass.Fset)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
_ = foldl(inspect, null{}, pass.Files)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// render returns the pretty-print of the given node
|
||||
func render(fset *token.FileSet, x interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
if err := printer.Fprint(&buf, fset, x); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
\end{gocode}
|
||||
\caption{The refactored, functional prettyprint implementation\autocite{prettyprint-functional}}
|
||||
\end{code}
|
||||
|
||||
\section{Imitating Sum types in Go}\label{appendix:sum-types}
|
||||
|
||||
\begin{code}
|
||||
\gofile{../work/examples/sumtypes/main.go}%
|
||||
\caption{Demonstration of how sum types can be imitated with interfaces}
|
||||
\end{code}
|
||||
|
||||
\section{Compiling and using functional Go}\label{appendix:install-fgo}
|
||||
|
||||
To compile and use the changes to the Go compiler that have been implemented in
|
||||
this thesis, these instructions should be followed.
|
||||
|
||||
First, check out the Go source code:
|
||||
|
||||
\begin{bashcode}
|
||||
$> git clone https://github.com/tommyknows/go.git && cd go
|
||||
$> git checkout bachelor-thesis
|
||||
\end{bashcode}
|
||||
|
||||
\subsection{With Docker}
|
||||
|
||||
If Docker is installed on your system, you can follow these steps from within
|
||||
the checked out `go' git repository on the branch `bachelor-thesis'.
|
||||
The downside of this approach is that it complicates building and sharing binaries.
|
||||
To compile your own project, the directory has to be mounted into the container.
|
||||
If your Guest OS is not Linux, cross-compilation is required so that the executable
|
||||
can be ran on the host.
|
||||
|
||||
These steps have been wrapped inside a script that prints out the necessary commands
|
||||
to configure your environment.
|
||||
|
||||
\begin{bashcode}
|
||||
$> eval "$(./setup-docker.sh)"
|
||||
\end{bashcode}
|
||||
The commands within the shell script will
|
||||
build Go in the container and print commands to configure the environment. The `eval'
|
||||
command then executes these printed commands. Executing this command may take a while,
|
||||
as the Go compiler is compiled within this process.
|
||||
|
||||
To build projects with the functional Go installation, simply use `fgo' on the command line.
|
||||
An alias has been created that mounts the current directory and executes the `fgo'
|
||||
command within the container.
|
||||
|
||||
Note that if this only configures the `fgo' command in the current shell session. To
|
||||
persist it across shell-sessions, execute the script without eval:
|
||||
\begin{bashcode}
|
||||
$> ./setup-docker.sh
|
||||
\end{bashcode}
|
||||
|
||||
And add the printed commands to your `.bashrc' (or equivalent). Further, you may
|
||||
also need to change the binary path from `/tmp/fgo/bin' to a path which is not
|
||||
cleaned up regularly.
|
||||
|
||||
\subsection{With a working Go installation}
|
||||
|
||||
If you already have a working Go installation on your system, the following
|
||||
steps provide a way to get functional Go up and running in the same way
|
||||
a normal go installation does.
|
||||
|
||||
These steps need to be executed from within the checked out `go' git
|
||||
repository on the branch `bachelor-thesis'.
|
||||
|
||||
Build the functional Go binary and configure the environment:
|
||||
\begin{bashcode}
|
||||
$> cd ./src
|
||||
$> ./make.bash
|
||||
$> ln -s $(realpath $(pwd)/../bin/go) /usr/local/bin/fgo
|
||||
$> go env -w GOROOT=$(realpath $(pwd)/..)
|
||||
\end{bashcode}
|
||||
|
||||
The `go env' command sets the GOROOT to point to the newly compiled tools
|
||||
and source code and is valid for the current shell session only.
|
||||
|
||||
\subsection{Using the installation}
|
||||
|
||||
After these steps, the binary (or alias) `fgo' can be used to test and build
|
||||
functional Go code. `fgo' is not different to the normal `go' command, so
|
||||
all commands that work with the normal `go' command also work with
|
||||
the `fgo' command.
|
||||
|
||||
\begin{bashcode}
|
||||
$> cd <code directory>
|
||||
$> fgo test ./...
|
||||
$> fgo build ./...
|
||||
\end{bashcode}
|
||||
|
||||
|
||||
\section{Building Funcheck}\label{appendix:build-funcheck}
|
||||
|
||||
Funcheck needs to be built against functional Go to properly detect the builtin functions.
|
||||
If you have not done so already, install `fgo' as shown in Appendix~\ref{appendix:install-fgo}.
|
||||
|
||||
Then, funcheck can be installed directly with `go get' (or rather, `fgo get'). `go get'
|
||||
downloads the source code to the Go modules directory (usually in \mintinline{bash}|$GOPATH/pkg/mod|),
|
||||
compiles the specified package and moves the binary to \mintinline{bash}|$GOPATH/bin|.
|
||||
|
||||
\begin{bashcode}
|
||||
$> # go get should not be called from within a go module
|
||||
$> cd /tmp
|
||||
$> fgo get github.com/tommyknows/funcheck
|
||||
$> funcheck -h
|
||||
\end{bashcode}
|
||||
|
||||
This installs `funcheck' into \mintinline{bash}|$GOBIN| or, if \mintinline{bash}|$GOBIN|
|
||||
is not set, into \mintinline{bash}|$GOPATH/bin|.
|
||||
|
||||
You can also clone the git repository and use `fgo build' to build `funcheck':
|
||||
\begin{bashcode}
|
||||
$> git clone https://github.com/tommyknows/funcheck.git
|
||||
$> cd funcheck
|
||||
$> fgo build .
|
||||
$> mv ./funcheck /usr/local/bin/funcheck
|
||||
$> funcheck -h
|
||||
\end{bashcode}
|
||||
|
||||
To run funcheck against the current directory / package, simply run
|
||||
\begin{bashcode}
|
||||
$> funcheck .
|
||||
\end{bashcode}
|
||||
|
||||
\section{Building Gopls}\label{appendix:build-gopls}
|
||||
|
||||
Gopls is the official language server for Go. Similar to funcheck, there are two options
|
||||
to install it on your local machine.
|
||||
|
||||
Installing with `go get':
|
||||
\begin{bashcode}
|
||||
$> # go get should not be called from within a go module
|
||||
$> cd /tmp
|
||||
$> fgo get golang.org/x/tools/gopls
|
||||
$> gopls -h
|
||||
\end{bashcode}
|
||||
|
||||
Or by downloading the source manually:
|
||||
\begin{bashcode}
|
||||
$> git clone https://github.com/golang/tools.git
|
||||
$> cd ./tools/gopls
|
||||
$> fgo build .
|
||||
$> mv ./gopls /usr/local/bin/gopls
|
||||
$> gopls -h
|
||||
\end{bashcode}
|
||||
|
||||
% generate a chapter without the chapter heading
|
||||
\clearpage
|
||||
\phantomsection
|
||||
\addtocounter{section}{1}
|
||||
\addcontentsline{toc}{section}{%
|
||||
\protect\numberline{\thesection}%
|
||||
Assignment}
|
||||
\includepdf[pages=-]{assignment.pdf}
|
After Width: | Height: | Size: 228 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,46 @@
|
||||
introduction:
|
||||
- Functional Programming Hype
|
||||
- Haskell => functional par excellence:
|
||||
- examples
|
||||
- learning curve extremely steep, both for syntax & paradigm
|
||||
- Conclusion; Easier, more familiar (& verbose) introduction to FP needed:
|
||||
- should not be "productive", but easy to learn FP
|
||||
- Cleary state the goal; Educational language
|
||||
- Why choose Go:
|
||||
- Easy & familiar syntax (C)
|
||||
- GC (Rust too complicated, though more powerful TS for FP)
|
||||
- Existing Work:
|
||||
- Talk by Francesc @ GopherCon; functional Go
|
||||
- Downsides of Go:
|
||||
- type system (polymorphism, sum types)
|
||||
- no Lists
|
||||
- no FP enforcement
|
||||
- Examples to everything, possibly comparison to Haskell?
|
||||
- TCO / benchmarking?
|
||||
- Work to be done / mitigation:
|
||||
- Powerful list implementation (polymorphic, supported by compiler) (Syntax TBD)
|
||||
- FuncCheck (Rules TBD)
|
||||
related_work:
|
||||
- ???
|
||||
-------- DONE --------
|
||||
methodology:
|
||||
- list functions implementation:
|
||||
- plan, benchmarks, constraints & limitations, goal
|
||||
- which functions:
|
||||
- map (name? fmap?)
|
||||
- prepend (similar to append, but way more expensive :-) )
|
||||
- filter
|
||||
- reduce
|
||||
- zip
|
||||
- funccheck:
|
||||
- research "rules" for FP
|
||||
- compare them
|
||||
- create a curated list of rules
|
||||
- implement those in a linter
|
||||
- inspect results
|
||||
application:
|
||||
- ???
|
||||
experiments_and_results:
|
||||
- ???
|
||||
discussion:
|
||||
- ???
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 119 KiB |
@ -0,0 +1,6 @@
|
||||
- Cite Wikipedia?
|
||||
- Personal statements & opinions? (clearly marked)
|
||||
- citation for things like "these features are also what makes Haskell famously hard to learn."
|
||||
- cite Blog posts? (e.g. stackoverflow.blog)
|
||||
- BA rights?
|
||||
- should or will?
|
@ -0,0 +1,8 @@
|
||||
[0] Config.pm:304> INFO - This is Biber 2.14 running in TOOL mode
|
||||
[0] Config.pm:307> INFO - Logfile is 'thesis.bib.blg'
|
||||
[88] biber-darwin:322> INFO - === Fri Apr 10, 2020, 13:34:33
|
||||
[102] Utils.pm:75> INFO - Globbing data source 'thesis.bib'
|
||||
[102] Utils.pm:91> INFO - Globbed data source 'thesis.bib' to thesis.bib
|
||||
[113] Biber.pm:4451> INFO - Looking for bibtex format file 'thesis.bib'
|
||||
[114] bibtex.pm:1653> INFO - LaTeX decoding ...
|
||||
[152] bibtex.pm:1471> INFO - Found BibTeX data source 'thesis.bib'
|
@ -0,0 +1,7 @@
|
||||
This is makeindex, version 2.15 [TeX Live 2019] (kpathsea + Thai support).
|
||||
Scanning style file ./thesis.ist.............................done (29 attributes redefined, 0 ignored).
|
||||
Scanning input file thesis.glo....done (11 entries accepted, 0 rejected).
|
||||
Sorting entries....done (44 comparisons).
|
||||
Generating output file thesis.gls....done (25 lines written, 0 warnings).
|
||||
Output written in thesis.gls.
|
||||
Transcript written in thesis.glg.
|
@ -0,0 +1,11 @@
|
||||
\glossaryentry{AST?\glossentry{ast}|setentrycounter[]{page}\glsnumberformat}{16}
|
||||
\glossaryentry{SSA?\glossentry{ssa}|setentrycounter[]{page}\glsnumberformat}{16}
|
||||
\glossaryentry{DAG?\glossentry{dag}|setentrycounter[]{page}\glsnumberformat}{16}
|
||||
\glossaryentry{copy by value?\glossentry{copy-by-value}|setentrycounter[]{page}\glsnumberformat}{27}
|
||||
\glossaryentry{EBNF?\glossentry{ebnf}|setentrycounter[]{page}\glsnumberformat}{32}
|
||||
\glossaryentry{AST?\glossentry{ast}|setentrycounter[]{page}\glsnumberformat}{35}
|
||||
\glossaryentry{SSA?\glossentry{ssa}|setentrycounter[]{page}\glsnumberformat}{35}
|
||||
\glossaryentry{stdout?\glossentry{stdout}|setentrycounter[]{page}\glsnumberformat}{54}
|
||||
\glossaryentry{AST?\glossentry{ast}|setentrycounter[]{page}\glsnumberformat}{62}
|
||||
\glossaryentry{AST?\glossentry{ast}|setentrycounter[]{page}\glsnumberformat}{63}
|
||||
\glossaryentry{sum types?\glossentry{sumtypes}|setentrycounter[]{page}\glsnumberformat}{65}
|
@ -0,0 +1,25 @@
|
||||
\glossarysection[\glossarytoctitle]{\glossarytitle}\glossarypreamble
|
||||
\begin{theglossary}\glossaryheader
|
||||
\glsgroupheading{A}\relax \glsresetentrylist %
|
||||
\glossentry{ast}{\glossaryentrynumbers{\relax
|
||||
\setentrycounter[]{page}\glsnumberformat{16}\delimN
|
||||
\setentrycounter[]{page}\glsnumberformat{35}\delimN
|
||||
\setentrycounter[]{page}\glsnumberformat{62\delimN 63}}}\glsgroupskip
|
||||
\glsgroupheading{C}\relax \glsresetentrylist %
|
||||
\glossentry{copy-by-value}{\glossaryentrynumbers{\relax
|
||||
\setentrycounter[]{page}\glsnumberformat{27}}}\glsgroupskip
|
||||
\glsgroupheading{D}\relax \glsresetentrylist %
|
||||
\glossentry{dag}{\glossaryentrynumbers{\relax
|
||||
\setentrycounter[]{page}\glsnumberformat{16}}}\glsgroupskip
|
||||
\glsgroupheading{E}\relax \glsresetentrylist %
|
||||
\glossentry{ebnf}{\glossaryentrynumbers{\relax
|
||||
\setentrycounter[]{page}\glsnumberformat{32}}}\glsgroupskip
|
||||
\glsgroupheading{S}\relax \glsresetentrylist %
|
||||
\glossentry{ssa}{\glossaryentrynumbers{\relax
|
||||
\setentrycounter[]{page}\glsnumberformat{16}\delimN
|
||||
\setentrycounter[]{page}\glsnumberformat{35}}}%
|
||||
\glossentry{stdout}{\glossaryentrynumbers{\relax
|
||||
\setentrycounter[]{page}\glsnumberformat{54}}}%
|
||||
\glossentry{sumtypes}{\glossaryentrynumbers{\relax
|
||||
\setentrycounter[]{page}\glsnumberformat{65}}}%
|
||||
\end{theglossary}\glossarypostamble
|
@ -0,0 +1,210 @@
|
||||
\ifglsentryexists{dag}{}%
|
||||
{%
|
||||
\gls@defglossaryentry{dag}%
|
||||
{%
|
||||
name={DAG},%
|
||||
sort={DAG},%
|
||||
type={main},%
|
||||
first={DAG},%
|
||||
firstplural={DAGs},%
|
||||
text={DAG},%
|
||||
plural={DAGs},%
|
||||
description={Directed Acyclic Graph},%
|
||||
descriptionplural={Directed Acyclic Graph},%
|
||||
symbol={\relax },%
|
||||
symbolplural={\relax },%
|
||||
user1={},%
|
||||
user2={},%
|
||||
user3={},%
|
||||
user4={},%
|
||||
user5={},%
|
||||
user6={},%
|
||||
long={},%
|
||||
longplural={},%
|
||||
short={},%
|
||||
shortplural={},%
|
||||
counter={page},%
|
||||
parent={},%
|
||||
%
|
||||
}%
|
||||
}%
|
||||
\ifglsentryexists{ast}{}%
|
||||
{%
|
||||
\gls@defglossaryentry{ast}%
|
||||
{%
|
||||
name={AST},%
|
||||
sort={AST},%
|
||||
type={main},%
|
||||
first={AST},%
|
||||
firstplural={ASTs},%
|
||||
text={AST},%
|
||||
plural={ASTs},%
|
||||
description={Abstract Syntax Tree, an abstract representation of source code as a tree},%
|
||||
descriptionplural={Abstract Syntax Tree, an abstract representation of source code as a tree},%
|
||||
symbol={\relax },%
|
||||
symbolplural={\relax },%
|
||||
user1={},%
|
||||
user2={},%
|
||||
user3={},%
|
||||
user4={},%
|
||||
user5={},%
|
||||
user6={},%
|
||||
long={},%
|
||||
longplural={},%
|
||||
short={},%
|
||||
shortplural={},%
|
||||
counter={page},%
|
||||
parent={},%
|
||||
%
|
||||
}%
|
||||
}%
|
||||
\ifglsentryexists{ssa}{}%
|
||||
{%
|
||||
\gls@defglossaryentry{ssa}%
|
||||
{%
|
||||
name={SSA},%
|
||||
sort={SSA},%
|
||||
type={main},%
|
||||
first={SSA},%
|
||||
firstplural={SSAs},%
|
||||
text={SSA},%
|
||||
plural={SSAs},%
|
||||
description={Single Static Assignment, an intermediate representation between the AST and the compiled binary that simplifies and improves compiler optimisations},%
|
||||
descriptionplural={Single Static Assignment, an intermediate representation between the AST and the compiled binary that simplifies and improves compiler optimisations},%
|
||||
symbol={\relax },%
|
||||
symbolplural={\relax },%
|
||||
user1={},%
|
||||
user2={},%
|
||||
user3={},%
|
||||
user4={},%
|
||||
user5={},%
|
||||
user6={},%
|
||||
long={},%
|
||||
longplural={},%
|
||||
short={},%
|
||||
shortplural={},%
|
||||
counter={page},%
|
||||
parent={},%
|
||||
%
|
||||
}%
|
||||
}%
|
||||
\ifglsentryexists{copy-by-value}{}%
|
||||
{%
|
||||
\gls@defglossaryentry{copy-by-value}%
|
||||
{%
|
||||
name={copy by value},%
|
||||
sort={copy by value},%
|
||||
type={main},%
|
||||
first={copy by value},%
|
||||
firstplural={copy by values},%
|
||||
text={copy by value},%
|
||||
plural={copy by values},%
|
||||
description={Copy by value refers to the argument-passing style where the supplied arguments are copied. To pass references in Go, the developer needs to use pointers instead},%
|
||||
descriptionplural={Copy by value refers to the argument-passing style where the supplied arguments are copied. To pass references in Go, the developer needs to use pointers instead},%
|
||||
symbol={\relax },%
|
||||
symbolplural={\relax },%
|
||||
user1={},%
|
||||
user2={},%
|
||||
user3={},%
|
||||
user4={},%
|
||||
user5={},%
|
||||
user6={},%
|
||||
long={},%
|
||||
longplural={},%
|
||||
short={},%
|
||||
shortplural={},%
|
||||
counter={page},%
|
||||
parent={},%
|
||||
%
|
||||
}%
|
||||
}%
|
||||
\ifglsentryexists{ebnf}{}%
|
||||
{%
|
||||
\gls@defglossaryentry{ebnf}%
|
||||
{%
|
||||
name={EBNF},%
|
||||
sort={EBNF},%
|
||||
type={main},%
|
||||
first={EBNF},%
|
||||
firstplural={EBNFs},%
|
||||
text={EBNF},%
|
||||
plural={EBNFs},%
|
||||
description={Extended Backus-Naur Form, an extended version of the `Backus-Naur Form, a notation technique to describe programming language syntax},%
|
||||
descriptionplural={Extended Backus-Naur Form, an extended version of the `Backus-Naur Form, a notation technique to describe programming language syntax},%
|
||||
symbol={\relax },%
|
||||
symbolplural={\relax },%
|
||||
user1={},%
|
||||
user2={},%
|
||||
user3={},%
|
||||
user4={},%
|
||||
user5={},%
|
||||
user6={},%
|
||||
long={},%
|
||||
longplural={},%
|
||||
short={},%
|
||||
shortplural={},%
|
||||
counter={page},%
|
||||
parent={},%
|
||||
%
|
||||
}%
|
||||
}%
|
||||
\ifglsentryexists{stdout}{}%
|
||||
{%
|
||||
\gls@defglossaryentry{stdout}%
|
||||
{%
|
||||
name={stdout},%
|
||||
sort={stdout},%
|
||||
type={main},%
|
||||
first={stdout},%
|
||||
firstplural={stdouts},%
|
||||
text={stdout},%
|
||||
plural={stdouts},%
|
||||
description={Standard Output, the default output stream for programs},%
|
||||
descriptionplural={Standard Output, the default output stream for programs},%
|
||||
symbol={\relax },%
|
||||
symbolplural={\relax },%
|
||||
user1={},%
|
||||
user2={},%
|
||||
user3={},%
|
||||
user4={},%
|
||||
user5={},%
|
||||
user6={},%
|
||||
long={},%
|
||||
longplural={},%
|
||||
short={},%
|
||||
shortplural={},%
|
||||
counter={page},%
|
||||
parent={},%
|
||||
%
|
||||
}%
|
||||
}%
|
||||
\ifglsentryexists{sumtypes}{}%
|
||||
{%
|
||||
\gls@defglossaryentry{sumtypes}%
|
||||
{%
|
||||
name={sum types},%
|
||||
sort={sum types},%
|
||||
type={main},%
|
||||
first={sum types},%
|
||||
firstplural={sum typess},%
|
||||
text={sum types},%
|
||||
plural={sum typess},%
|
||||
description={Sum types, often also called aggregated types, variant or tagged union is a data structure that can hold one of several, predefined data types. For example, Haskell's \mintinline {haskell}|Either| holds either a value of type A or type B. Similar to that, \mintinline {haskell}|Maybe| can hold either a concrete value, or `Nothing'},%
|
||||
descriptionplural={Sum types, often also called aggregated types, variant or tagged union is a data structure that can hold one of several, predefined data types. For example, Haskell's \mintinline {haskell}|Either| holds either a value of type A or type B. Similar to that, \mintinline {haskell}|Maybe| can hold either a concrete value, or `Nothing'},%
|
||||
symbol={\relax },%
|
||||
symbolplural={\relax },%
|
||||
user1={},%
|
||||
user2={},%
|
||||
user3={},%
|
||||
user4={},%
|
||||
user5={},%
|
||||
user6={},%
|
||||
long={},%
|
||||
longplural={},%
|
||||
short={},%
|
||||
shortplural={},%
|
||||
counter={page},%
|
||||
parent={},%
|
||||
%
|
||||
}%
|
||||
}%
|
@ -0,0 +1,2 @@
|
||||
$ gopls check main.go
|
||||
/tmp/playground/main.go:6:22-23: cannot convert 3 (untyped int constant) to string
|
@ -0,0 +1,81 @@
|
||||
\babel@toc {english}{}
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\contentsline {listing}{\numberline {1.1}{\ignorespaces Quicksort implementation in Haskell\relax }}{5}{listing.caption.5}%
|
||||
\contentsline {listing}{\numberline {1.2}{\ignorespaces Go web server handler function\relax }}{7}{listing.caption.6}%
|
||||
\contentsline {listing}{\numberline {1.3}{\ignorespaces Constructor with functional options\relax }}{8}{listing.caption.7}%
|
||||
\contentsline {listing}{\numberline {1.4}{\ignorespaces Example for a functional option\relax }}{8}{listing.caption.8}%
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\contentsline {listing}{\numberline {3.1}{\ignorespaces Example usage for map and fmap\relax }}{20}{listing.caption.10}%
|
||||
\contentsline {listing}{\numberline {3.2}{\ignorespaces Example usage of map in Go\relax }}{21}{listing.caption.11}%
|
||||
\contentsline {listing}{\numberline {3.3}{\ignorespaces Example usage of prepend in go\relax }}{22}{listing.caption.13}%
|
||||
\contentsline {listing}{\numberline {3.4}{\ignorespaces Function headers of the fold functions\relax }}{23}{listing.caption.14}%
|
||||
\contentsline {listing}{\numberline {3.5}{\ignorespaces foldr and foldl execution order\relax }}{23}{listing.caption.16}%
|
||||
\contentsline {listing}{\numberline {3.6}{\ignorespaces Example usage of foldr and foldl in go\relax }}{24}{listing.caption.17}%
|
||||
\contentsline {listing}{\numberline {3.7}{\ignorespaces Example usage of filter in Go\relax }}{25}{listing.caption.18}%
|
||||
\contentsline {listing}{\numberline {3.8}{\ignorespaces Go Variable Declarations\relax }}{30}{listing.caption.19}%
|
||||
\contentsline {listing}{\numberline {3.9}{\ignorespaces Go Assignment Operators\relax }}{31}{listing.caption.20}%
|
||||
\contentsline {listing}{\numberline {3.10}{\ignorespaces Go scoping issue with recursive functions\relax }}{32}{listing.caption.22}%
|
||||
\contentsline {listing}{\numberline {3.11}{\ignorespaces Fixing the scope issue on recursive functions\relax }}{32}{listing.caption.23}%
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\contentsline {listing}{\numberline {4.1}{\ignorespaces Godoc for the new built-in functions\autocite {new-builtins-godoc}\relax }}{37}{listing.caption.27}%
|
||||
\contentsline {listing}{\numberline {4.2}{\ignorespaces Registering new built-in functions\autocite {new-builtins-universe}\relax }}{38}{listing.caption.28}%
|
||||
\contentsline {listing}{\numberline {4.3}{\ignorespaces fmap implementation in Go\relax }}{40}{listing.caption.30}%
|
||||
\contentsline {listing}{\numberline {4.4}{\ignorespaces Improved implementation of fmap\relax }}{40}{listing.caption.31}%
|
||||
\contentsline {listing}{\numberline {4.5}{\ignorespaces fmap AST translation\autocite {fmap-walk-implementation}\relax }}{41}{listing.caption.32}%
|
||||
\contentsline {listing}{\numberline {4.6}{\ignorespaces prepend implementation in Go\relax }}{41}{listing.caption.34}%
|
||||
\contentsline {listing}{\numberline {4.7}{\ignorespaces prepend AST translation\autocite {prepend-walk-implementation}\relax }}{42}{listing.caption.35}%
|
||||
\contentsline {listing}{\numberline {4.8}{\ignorespaces fold implementation in Go\relax }}{42}{listing.caption.37}%
|
||||
\contentsline {listing}{\numberline {4.9}{\ignorespaces fold AST translation\autocite {fold-walk-implementation}\relax }}{43}{listing.caption.38}%
|
||||
\contentsline {listing}{\numberline {4.10}{\ignorespaces filter implementation in Go\relax }}{43}{listing.caption.40}%
|
||||
\contentsline {listing}{\numberline {4.11}{\ignorespaces filter AST translation\autocite {filter-walk-implementation}\relax }}{44}{listing.caption.41}%
|
||||
\contentsline {listing}{\numberline {4.12}{\ignorespaces Illustrating the difference between Go code and it's AST code\relax }}{45}{listing.caption.43}%
|
||||
\contentsline {listing}{\numberline {4.13}{\ignorespaces Handling the basic AST types in funcheck\autocite {funcheck-ast-types}\relax }}{49}{listing.caption.44}%
|
||||
\contentsline {listing}{\numberline {4.14}{\ignorespaces Illustration of an assignment node and corresponding positions\autocite {ast-positions}\relax }}{50}{listing.caption.45}%
|
||||
\contentsline {listing}{\numberline {4.15}{\ignorespaces Illustration of a function literal assignment\autocite {ast-positions}\relax }}{51}{listing.caption.46}%
|
||||
\contentsline {listing}{\numberline {4.16}{\ignorespaces Testing a code analyser with the `analysistest' package\relax }}{52}{listing.caption.47}%
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\contentsline {listing}{\numberline {5.1}{\ignorespaces Demonstration of the new built-in functions\relax }}{54}{listing.caption.48}%
|
||||
\contentsline {listing}{\numberline {5.2}{\ignorespaces Pretty-printing declarations in idiomatic Go\relax }}{56}{listing.caption.49}%
|
||||
\contentsline {listing}{\numberline {5.3}{\ignorespaces Pretty-printing declarations in functional Go\relax }}{57}{listing.caption.50}%
|
||||
\contentsline {listing}{\numberline {5.4}{\ignorespaces Quicksort implementations compared\relax }}{58}{listing.caption.51}%
|
||||
\contentsline {listing}{\numberline {5.5}{\ignorespaces Comparison Java Streams and functional Go\relax }}{59}{listing.caption.52}%
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\addvspace {10\p@ }
|
||||
\contentsline {listing}{\numberline {1}{\ignorespaces Functional Options for a simple web server\relax }}{81}{listing.caption.61}%
|
||||
\contentsline {listing}{\numberline {2}{\ignorespaces Example on how to mutate data in Go\relax }}{83}{listing.caption.62}%
|
||||
\contentsline {listing}{\numberline {3}{\ignorespaces Example on how shadowing works on block scopes\relax }}{84}{listing.caption.63}%
|
||||
\contentsline {listing}{\numberline {4}{\ignorespaces foldl and foldl' strictness\autocite {fold-types}\relax }}{85}{listing.caption.64}%
|
||||
\contentsline {listing}{\numberline {5}{\ignorespaces Working around the missing foldl implementation in Go\relax }}{86}{listing.caption.65}%
|
||||
\contentsline {listing}{\numberline {6}{\ignorespaces The original prettyprint implementation\autocite {prettyprint-orig}\relax }}{90}{listing.caption.66}%
|
||||
\contentsline {listing}{\numberline {7}{\ignorespaces The refactored, functional prettyprint implementation\autocite {prettyprint-functional}\relax }}{92}{listing.caption.67}%
|
||||
\contentsline {listing}{\numberline {8}{\ignorespaces Demonstration of how sum types can be imitated with interfaces\relax }}{93}{listing.caption.68}%
|
@ -0,0 +1,123 @@
|
||||
%!
|
||||
/pdfmark where{pop}
|
||||
{/globaldict where{pop globaldict}{userdict}ifelse/pdfmark/cleartomark load put}
|
||||
ifelse
|
||||
[
|
||||
/Title(\376\377\000S\000u\000m\000m\000a\000r\000y)
|
||||
/Action/GoTo/Dest(chapter*.1)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\000A\000b\000s\000t\000r\000a\000c\000t)
|
||||
/Action/GoTo/Dest(chapter*.3)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\000P\000r\000e\000f\000a\000c\000e)
|
||||
/Action/GoTo/Dest(chapter*.5)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0001\000\040\000I\000n\000t\000r\000o\000d\000u\000c\000t\000i\000o\000n)
|
||||
/Count 6
|
||||
/Action/GoTo/Dest(chapter.1)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0001\000.\0001\000\040\000L\000e\000a\000r\000n\000i\000n\000g\000\040\000F\000u\000n\000c\000t\000i\000o\000n\000a\000l\000\040\000P\000r\000o\000g\000r\000a\000m\000m\000i\000n\000g)
|
||||
/Action/GoTo/Dest(section.1.1)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0001\000.\0002\000\040\000H\000a\000s\000k\000e\000l\000l)
|
||||
/Action/GoTo/Dest(section.1.2)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0001\000.\0003\000\040\000G\000o\000a\000l\000s)
|
||||
/Action/GoTo/Dest(section.1.3)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0001\000.\0004\000\040\000W\000h\000y\000\040\000G\000o)
|
||||
/Count 1
|
||||
/Action/GoTo/Dest(section.1.4)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0001\000.\0004\000.\0001\000\040\000G\000o\000\040\000S\000l\000i\000c\000e\000s)
|
||||
/Action/GoTo/Dest(subsection.1.4.1)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0001\000.\0005\000\040\000E\000x\000i\000s\000t\000i\000n\000g\000\040\000W\000o\000r\000k)
|
||||
/Action/GoTo/Dest(section.1.5)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0001\000.\0006\000\040\000W\000o\000r\000k\000\040\000t\000o\000\040\000b\000e\000\040\000d\000o\000n\000e)
|
||||
/Action/GoTo/Dest(section.1.6)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0002\000\040\000M\000e\000t\000h\000o\000d\000o\000l\000o\000g\000y)
|
||||
/Count 2
|
||||
/Action/GoTo/Dest(chapter.2)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0002\000.\0001\000\040\000S\000l\000i\000c\000e\000\040\000H\000e\000l\000p\000e\000r\000\040\000F\000u\000n\000c\000t\000i\000o\000n\000s)
|
||||
/Count 3
|
||||
/Action/GoTo/Dest(section.2.1)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0002\000.\0001\000.\0001\000\040\000C\000h\000o\000o\000s\000i\000n\000g\000\040\000t\000h\000e\000\040\000f\000u\000n\000c\000t\000i\000o\000n\000s)
|
||||
/Action/GoTo/Dest(subsection.2.1.1)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0002\000.\0001\000.\0002\000\040\000R\000e\000q\000u\000i\000r\000e\000d\000\040\000S\000t\000e\000p\000s)
|
||||
/Action/GoTo/Dest(subsection.2.1.2)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0002\000.\0001\000.\0003\000\040\000P\000r\000e\000p\000e\000n\000d)
|
||||
/Action/GoTo/Dest(subsection.2.1.3)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0002\000.\0002\000\040\000F\000u\000n\000c\000t\000i\000o\000n\000a\000l\000\040\000C\000h\000e\000c\000k)
|
||||
/Action/GoTo/Dest(section.2.2)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0003\000\040\000I\000m\000p\000l\000e\000m\000e\000n\000t\000a\000t\000i\000o\000n)
|
||||
/Count 1
|
||||
/Action/GoTo/Dest(chapter.3)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0003\000.\0001\000\040\000S\000l\000i\000c\000e\000\040\000H\000e\000l\000p\000e\000r\000\040\000F\000u\000n\000c\000t\000i\000o\000n\000s)
|
||||
/Count 1
|
||||
/Action/GoTo/Dest(section.3.1)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0003\000.\0001\000.\0001\000\040\000I\000m\000p\000l\000e\000m\000e\000n\000t\000i\000n\000g\000\040\000P\000r\000e\000p\000e\000n\000d)
|
||||
/Action/GoTo/Dest(subsection.3.1.1)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0004\000\040\000A\000p\000p\000l\000i\000c\000a\000t\000i\000o\000n)
|
||||
/Action/GoTo/Dest(chapter.4)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0005\000\040\000E\000x\000p\000e\000r\000i\000m\000e\000n\000t\000s\000\040\000a\000n\000d\000\040\000R\000e\000s\000u\000l\000t\000s)
|
||||
/Action/GoTo/Dest(chapter.5)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0006\000\040\000D\000i\000s\000c\000u\000s\000s\000i\000o\000n)
|
||||
/Action/GoTo/Dest(chapter.6)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\000L\000i\000s\000t\000\040\000o\000f\000\040\000s\000o\000u\000r\000c\000e\000\040\000c\000o\000d\000e\000s)
|
||||
/Action/GoTo/Dest(chapter*.26)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\000L\000i\000s\000t\000\040\000o\000f\000\040\000F\000i\000g\000u\000r\000e\000s)
|
||||
/Action/GoTo/Dest(chapter*.27)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\000L\000i\000s\000t\000\040\000o\000f\000\040\000T\000a\000b\000l\000e\000s)
|
||||
/Action/GoTo/Dest(chapter*.28)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\000A\000p\000p\000e\000n\000d\000i\000c\000e\000s)
|
||||
/Count 1
|
||||
/Action/GoTo/Dest(section*.31)cvn
|
||||
/OUT pdfmark
|
||||
[
|
||||
/Title(\376\377\0001\000\040\000A\000n\000a\000l\000y\000s\000i\000s\000\040\000o\000f\000\040\000f\000u\000n\000c\000t\000i\000o\000n\000\040\000o\000c\000c\000u\000r\000r\000e\000n\000c\000e\000s\000\040\000i\000n\000\040\000H\000a\000s\000k\000e\000l\000l\000\040\000c\000o\000d\000e)
|
||||
/Action/GoTo/Dest(section.1..1)cvn
|
||||
/OUT pdfmark
|
Binary file not shown.
@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<!-- logreq request file -->
|
||||
<!-- logreq version 1.0 / dtd version 1.0 -->
|
||||
<!-- Do not edit this file! -->
|
||||
<!DOCTYPE requests [
|
||||
<!ELEMENT requests (internal | external)*>
|
||||
<!ELEMENT internal (generic, (provides | requires)*)>
|
||||
<!ELEMENT external (generic, cmdline?, input?, output?, (provides | requires)*)>
|
||||
<!ELEMENT cmdline (binary, (option | infile | outfile)*)>
|
||||
<!ELEMENT input (file)+>
|
||||
<!ELEMENT output (file)+>
|
||||
<!ELEMENT provides (file)+>
|
||||
<!ELEMENT requires (file)+>
|
||||
<!ELEMENT generic (#PCDATA)>
|
||||
<!ELEMENT binary (#PCDATA)>
|
||||
<!ELEMENT option (#PCDATA)>
|
||||
<!ELEMENT infile (#PCDATA)>
|
||||
<!ELEMENT outfile (#PCDATA)>
|
||||
<!ELEMENT file (#PCDATA)>
|
||||
<!ATTLIST requests
|
||||
version CDATA #REQUIRED
|
||||
>
|
||||
<!ATTLIST internal
|
||||
package CDATA #REQUIRED
|
||||
priority (9) #REQUIRED
|
||||
active (0 | 1) #REQUIRED
|
||||
>
|
||||
<!ATTLIST external
|
||||
package CDATA #REQUIRED
|
||||
priority (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8) #REQUIRED
|
||||
active (0 | 1) #REQUIRED
|
||||
>
|
||||
<!ATTLIST provides
|
||||
type (static | dynamic | editable) #REQUIRED
|
||||
>
|
||||
<!ATTLIST requires
|
||||
type (static | dynamic | editable) #REQUIRED
|
||||
>
|
||||
<!ATTLIST file
|
||||
type CDATA #IMPLIED
|
||||
>
|
||||
]>
|
||||
<requests version="1.0">
|
||||
<internal package="biblatex" priority="9" active="0">
|
||||
<generic>latex</generic>
|
||||
<provides type="dynamic">
|
||||
<file>thesis.bcf</file>
|
||||
</provides>
|
||||
<requires type="dynamic">
|
||||
<file>thesis.bbl</file>
|
||||
</requires>
|
||||
<requires type="static">
|
||||
<file>blx-dm.def</file>
|
||||
<file>blx-compat.def</file>
|
||||
<file>biblatex.def</file>
|
||||
<file>standard.bbx</file>
|
||||
<file>numeric.bbx</file>
|
||||
<file>numeric-comp.bbx</file>
|
||||
<file>ieee.bbx</file>
|
||||
<file>numeric-comp.cbx</file>
|
||||
<file>ieee.cbx</file>
|
||||
<file>biblatex.cfg</file>
|
||||
<file>english.lbx</file>
|
||||
<file>german.lbx</file>
|
||||
<file>nswissgerman.lbx</file>
|
||||
</requires>
|
||||
</internal>
|
||||
<external package="biblatex" priority="5" active="0">
|
||||
<generic>biber</generic>
|
||||
<cmdline>
|
||||
<binary>biber</binary>
|
||||
<infile>thesis</infile>
|
||||
</cmdline>
|
||||
<input>
|
||||
<file>thesis.bcf</file>
|
||||
</input>
|
||||
<output>
|
||||
<file>thesis.bbl</file>
|
||||
</output>
|
||||
<provides type="dynamic">
|
||||
<file>thesis.bbl</file>
|
||||
</provides>
|
||||
<requires type="dynamic">
|
||||
<file>thesis.bcf</file>
|
||||
</requires>
|
||||
<requires type="editable">
|
||||
<file>thesis.bib</file>
|
||||
</requires>
|
||||
</external>
|
||||
</requests>
|
@ -0,0 +1,407 @@
|
||||
% -*- mode: latex; coding: utf-8 -*-
|
||||
% !TEX TS-program = pdflatexmk
|
||||
% !TEX encoding = UTF-8 Unicode
|
||||
|
||||
%\RequirePackage[hyphens]{url}
|
||||
\documentclass[%
|
||||
a4paper,
|
||||
twoside,
|
||||
numbers=noenddot,
|
||||
parskip=half,
|
||||
open=any,
|
||||
headsepline,
|
||||
english, % german, english
|
||||
ba % ba, pa
|
||||
]{zhawthesis}
|
||||
|
||||
\usepackage{etoolbox}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Parameters
|
||||
% - Adjust these to your needs:
|
||||
|
||||
\title{Functional Go}
|
||||
\subtitle{...An Easier Introduction to Functional Programming}
|
||||
\author{% Komma getrennt
|
||||
Ramon Rüttimann
|
||||
}
|
||||
\newcommand\twodigits[1]{\ifnum#1<10 0#1\else #1\fi}
|
||||
\date{\twodigits{\the\day}.\twodigits{\number\month}.\the\year}
|
||||
|
||||
\major{Computer Science} % Studiengang
|
||||
\zhawsemester{Spring 2020}
|
||||
\zhawinstitute{init}
|
||||
\zhawlogocolour{pantone2945} % pantone2945, cmyk, sw
|
||||
\mainsupervisor{Dr. G. Burkert}
|
||||
\subsupervisor{Dr. K. Rege}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Base packages used by the template (any commonly used packages)
|
||||
|
||||
%\PassOptionsToPackage{hyphens}{url}\usepackage{hyperref}
|
||||
|
||||
\usepackage{float}
|
||||
\usepackage{graphicx}
|
||||
\graphicspath{{figures/}}
|
||||
\DeclareGraphicsExtensions{.pdf,.png,.jpg,.gif}
|
||||
|
||||
\usepackage{tabularx}
|
||||
\usepackage{longtable}
|
||||
\usepackage{booktabs}
|
||||
\usepackage{todonotes}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Custom packages
|
||||
% - Add packages used by your thesis here:
|
||||
|
||||
%\usepackage{hyperref}
|
||||
%\PassOptionsToPackage{hyphens}{url}\usepackage{hyperref}
|
||||
|
||||
\hypersetup{
|
||||
colorlinks = true, %Colours links instead of ugly boxes
|
||||
%urlcolor = blue, %Colour for external hyperlinks
|
||||
linkcolor = blue, %Colour of internal links
|
||||
citecolor = blue %Colour of citations
|
||||
}
|
||||
\usepackage[newfloat]{minted}
|
||||
\usepackage{listings}
|
||||
\newenvironment{code}{\captionsetup{type=listing}}{}
|
||||
\SetupFloatingEnvironment{listing}{name=Source Code,placement=H}
|
||||
\definecolor{bg}{rgb}{0.95,0.95,0.95}
|
||||
\newminted{bash}{breaklines,breakbytoken,tabsize=2,bgcolor=bg}
|
||||
\newminted{bnf}{breaklines,breakbytoken,tabsize=2,bgcolor=bg}
|
||||
\newminted{c}{breaklines,breakbytoken,tabsize=2,bgcolor=bg}
|
||||
\newminted{go}{breaklines,breakbytoken,tabsize=2,bgcolor=bg}
|
||||
\newminted{haskell}{breaklines,breakbytoken,tabsize=2,bgcolor=bg}
|
||||
\newminted{java}{breaklines,breakbytoken,tabsize=2,bgcolor=bg}
|
||||
\newmintedfile{go}{breaklines,breakanywhere,tabsize=2,bgcolor=bg,linenos,stepnumber=5,numberfirstline}
|
||||
|
||||
|
||||
\newcommand{\gofilerange}[4][]{%
|
||||
\immediate\write18{./utils/delim -file="#2" -start="#3" -end="#4"}%
|
||||
\IfFileExists{code.lineno}%
|
||||
{\CatchFileEdef{\linenumber}{./code.lineno}{\endlinechar=-1 }}%
|
||||
{\def\linenumber{0}}%
|
||||
\edef\flags{firstnumber=\linenumber,#1}%
|
||||
\expandafter\gofile\expandafter[\flags]{./code.snippet}}
|
||||
|
||||
\newcommand{\unchapter}[1]{%
|
||||
\begingroup
|
||||
\let\@makesectionhead\@gobble % make \@makechapterhead do nothing
|
||||
\section{#1}
|
||||
\endgroup
|
||||
}
|
||||
\makeatother
|
||||
%\sloppy
|
||||
|
||||
\usepackage[
|
||||
backend=biber,
|
||||
hyperref=true,
|
||||
style=ieee,
|
||||
dashed=false,
|
||||
]{biblatex}
|
||||
\usepackage{xurl}
|
||||
\usepackage[skip=0pt]{caption}
|
||||
\usepackage[toc,page]{appendix}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{glossaries}
|
||||
|
||||
\usepackage{pdfpages}
|
||||
%\usepackage{listings}
|
||||
\makeglossaries
|
||||
%\usepackage{csquotes}
|
||||
\AtBeginEnvironment{quote}{\itshape}
|
||||
%\bibliography{thesis}
|
||||
\addbibresource{thesis.bib}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\frontmatter
|
||||
|
||||
\maketitle
|
||||
|
||||
\cleardoublepage % chktex 1
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Declaration of Originality
|
||||
|
||||
\makedeclarationoforiginality % chktex 1
|
||||
|
||||
|
||||
\cleardoublepage % chktex 1
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
\chapter{Abstract}
|
||||
\label{ch:abstract} % chktex 24
|
||||
\input{chapters/01_abstract.tex}
|
||||
|
||||
\chapter{Zusammenfassung}
|
||||
\label{ch:summary} % chktex 24
|
||||
\input{chapters/02_zusammenfassung.tex}
|
||||
|
||||
\IfLanguageName{nswissgerman}{\chapter{Vorwort}}{\chapter{Preface}}
|
||||
\label{ch:preface} % chktex 24
|
||||
\input{chapters/10_preface.tex}
|
||||
|
||||
\cleardoublepage % chktex 1
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\mainmatter % chktex 1
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\IfLanguageName{nswissgerman}{\chapter{Einleitung}}{\chapter{Introduction}}
|
||||
\label{ch:introduction} % chktex 24
|
||||
\input{chapters/20_introduction.tex}
|
||||
|
||||
\chapter{About Go}
|
||||
\label{ch:about-go}
|
||||
\input{chapters/25_about_go.tex}
|
||||
|
||||
% DISABLE RELATED WORK AS I PUT THAT INTO THE INTRO
|
||||
%\IfLanguageName{nswissgerman}{\chapter{Verwandte Arbeit}}{\chapter{Related Work}}
|
||||
%\label{ch:related-work} % chktex 24
|
||||
%\input{chapters/30_related_work.tex}
|
||||
|
||||
\IfLanguageName{nswissgerman}{\chapter{Methoden}}{\chapter{Methodology}}
|
||||
\label{ch:methodology} % chktex 24
|
||||
\input{chapters/30_methodology.tex}
|
||||
|
||||
\chapter{Implementation}
|
||||
\label{ch:implementation} % chktex 24
|
||||
\input{chapters/40_implementation.tex}
|
||||
|
||||
\chapter{Application}
|
||||
\label{ch:application} % chktex 24
|
||||
\input{chapters/50_application.tex}
|
||||
|
||||
\IfLanguageName{nswissgerman}{\chapter{Resultate}}{\chapter{Results}}
|
||||
\label{ch:results} % chktex 24
|
||||
\input{chapters/60_results.tex}
|
||||
|
||||
\IfLanguageName{nswissgerman}{\chapter{Diskussion}}{\chapter{Discussion}}
|
||||
\label{ch:discussion} % chktex 24
|
||||
\input{chapters/70_discussion.tex}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\backmatter % chktex 1
|
||||
|
||||
% \let\clearpage\relax
|
||||
% \vspace{-4em}
|
||||
\printbibliography
|
||||
% \endgroup
|
||||
|
||||
\renewcommand{\lstlistlistingname}{List of source codes}
|
||||
|
||||
\lstlistoflistings
|
||||
|
||||
% \begingroup
|
||||
% \let\clearpage\relax
|
||||
% \vspace{-4em}
|
||||
\listoffigures
|
||||
% \endgroup
|
||||
|
||||
% \begingroup
|
||||
% \let\clearpage\relax
|
||||
% \vspace{-4em}
|
||||
\listoftables
|
||||
|
||||
\clearpage
|
||||
\phantomsection
|
||||
\addtocounter{chapter}{1}
|
||||
\addcontentsline{toc}{chapter}{%
|
||||
Glossary}
|
||||
\printglossaries
|
||||
% \endgroup
|
||||
\cleardoublepage % chktex 1
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%\appendix
|
||||
\begin{appendices}
|
||||
\input{chapters/80_appendix.tex}
|
||||
|
||||
%\section{Example for Functional Options}\label{appendix:funcopts}
|
||||
%\begin{code}
|
||||
%\captionof{listing}{Functional Options for a simple Webserver}
|
||||
%\gofile{../work/examples/functional-options/main.go}
|
||||
%\end{code}
|
||||
|
||||
%\section{Analysis of function occurrences in Haskell code}\label{appendix:function-occurrences}
|
||||
%The results of the analysis have been aquired by running the following command
|
||||
%from the root of the git repository\cite{git-repo}:
|
||||
%\begin{bashcode}
|
||||
%./work/common-list-functions/count-function.sh "map " " : " "fold" "filter " "reverse " "take " "drop " "maximum" "sum " "zip " "product " "minimum " "reduce "
|
||||
%\end{bashcode}
|
||||
|
||||
%\section{Mutating variables in Go}\label{appendix:mutation}
|
||||
%\begin{code}
|
||||
%\captionof{listing}{Example on how to mutate complex types in Go}
|
||||
%\gofile{../work/examples/mutate/main.go}
|
||||
%\end{code}
|
||||
|
||||
%\section{Shadowing variables in Go}\label{appendix:shadowing}
|
||||
%\begin{code}
|
||||
%\captionof{listing}{Example on how shadowing works on block scopes}
|
||||
%\gofile{../work/examples/shadowing/main.go}
|
||||
%\end{code}
|
||||
|
||||
%\section{Workaround for the missing foldl' implementation in Go}\label{appendix:foldl-go}
|
||||
%\begin{code}
|
||||
%\captionof{listing}{Working around the missing foldl implementation in Go}
|
||||
%\label{code:foldl-go}
|
||||
%\gofile{../work/examples/foldl-workaround/main.go}
|
||||
%\begin{bashcode}
|
||||
%$> fgo run .
|
||||
%0
|
||||
%panic: runtime error: invalid memory address or nil pointer dereference
|
||||
%[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x109e945]
|
||||
|
||||
%goroutine 1 [running]:
|
||||
%main.what(0x2, 0x0, 0x2)
|
||||
%/tmp/map/main.go:16 +0x5
|
||||
%main.main()
|
||||
%/tmp/map/main.go:12 +0x187
|
||||
%exit status 2
|
||||
%\end{bashcode}
|
||||
%\end{code}
|
||||
|
||||
%\section{Prettyprint implementation}\label{appendix:prettyprint-func}
|
||||
%\begin{code}
|
||||
%\captionof{listing}{The original prettyprint implementation}
|
||||
%\gofile{../work/funcheck/prettyprint/prettyprint.go}
|
||||
%\end{code}
|
||||
%\begin{code}
|
||||
%\captionof{listing}{The refactored, functional prettyprint implementation}
|
||||
%\begin{gocode}
|
||||
%package prettyprint
|
||||
|
||||
%import (
|
||||
%"bytes"
|
||||
%"fmt"
|
||||
%"go/ast"
|
||||
%"go/printer"
|
||||
%"go/token"
|
||||
|
||||
%"golang.org/x/tools/go/analysis"
|
||||
%)
|
||||
|
||||
%var Analyzer = &analysis.Analyzer{
|
||||
%Name: "prettyprint",
|
||||
%Doc: "prints positions",
|
||||
%Run: run,
|
||||
%}
|
||||
|
||||
%type null struct{}
|
||||
|
||||
%func checkDecl(as *ast.DeclStmt, fset *token.FileSet) {
|
||||
%fmt.Printf("Declaration %q: %v\n", render(fset, as), as.Pos())
|
||||
|
||||
%check := func(_ null, spec ast.Spec) (n null) {
|
||||
%val, ok := spec.(*ast.ValueSpec)
|
||||
%if !ok {
|
||||
%return
|
||||
%}
|
||||
%if val.Values != nil {
|
||||
%return
|
||||
%}
|
||||
%if _, ok := val.Type.(*ast.FuncType); !ok {
|
||||
%return
|
||||
%}
|
||||
%fmt.Printf("\tIdent %q: %v\n", render(fset, val), val.Names[0].Pos())
|
||||
%return
|
||||
%}
|
||||
|
||||
%if decl, ok := as.Decl.(*ast.GenDecl); ok {
|
||||
%_ = foldl(check, null{}, decl.Specs)
|
||||
%}
|
||||
%}
|
||||
|
||||
%func checkAssign(as *ast.AssignStmt, fset *token.FileSet) {
|
||||
%fmt.Printf("Assignment %q: %v\n", render(fset, as), as.Pos())
|
||||
|
||||
%check := func(_ null, expr ast.Expr) (n null) {
|
||||
%ident, ok := expr.(*ast.Ident) // Lhs always is an "IdentifierList"
|
||||
%if !ok {
|
||||
%return
|
||||
%}
|
||||
|
||||
%fmt.Printf("\tIdent %q: %v\n", ident.String(), ident.Pos())
|
||||
|
||||
%switch {
|
||||
%case ident.Name == "_":
|
||||
%fmt.Printf("\t\tBlank Identifier!\n")
|
||||
%case ident.Obj == nil:
|
||||
%fmt.Printf("\t\tDecl is not in the same file!\n")
|
||||
%default:
|
||||
%// make sure the declaration has a Pos func and get it
|
||||
%declPos := ident.Obj.Decl.(ast.Node).Pos()
|
||||
%fmt.Printf("\t\tDecl %q: %v\n", render(fset, ident.Obj.Decl), declPos)
|
||||
%}
|
||||
|
||||
%return
|
||||
%}
|
||||
%_ = foldl(check, null{}, as.Lhs)
|
||||
%}
|
||||
|
||||
%func run(pass *analysis.Pass) (interface{}, error) {
|
||||
%inspect := func(_ null, file *ast.File) (n null) {
|
||||
%ast.Inspect(file, func(n ast.Node) bool {
|
||||
%switch as := n.(type) {
|
||||
%case *ast.DeclStmt:
|
||||
%checkDecl(as, pass.Fset)
|
||||
%case *ast.AssignStmt:
|
||||
%checkAssign(as, pass.Fset)
|
||||
%}
|
||||
%return true
|
||||
%})
|
||||
%return
|
||||
%}
|
||||
%_ = foldl(inspect, null{}, pass.Files)
|
||||
|
||||
%return nil, nil
|
||||
%}
|
||||
|
||||
%// render returns the pretty-print of the given node
|
||||
%func render(fset *token.FileSet, x interface{}) string {
|
||||
%var buf bytes.Buffer
|
||||
%if err := printer.Fprint(&buf, fset, x); err != nil {
|
||||
%panic(err)
|
||||
%}
|
||||
%return buf.String()
|
||||
%}
|
||||
%\end{gocode}
|
||||
%\end{code}
|
||||
|
||||
%% - Add your appendix here:
|
||||
|
||||
%\todo[inline]{
|
||||
%Anhang/Appendix:
|
||||
|
||||
%\quad -- Projektmanagement: \\ % chktex 8
|
||||
%\qquad -- Offizielle Aufgabenstellung, Projektauftrag \\ % chktex 8
|
||||
%\qquad -- (Zeitplan) \\ % chktex 8
|
||||
%\qquad -- (Besprechungsprotokolle oder Journals) % chktex 8
|
||||
|
||||
%\quad -- Weiteres: \\ % chktex 8
|
||||
%\qquad -- CD/USB-Stick mit dem vollständigen Bericht als PDF-File inklusive Film- und Fotomaterial \\ % chktex 8
|
||||
%\qquad -- (Schaltpläne und Ablaufschemata) \\ % chktex 8
|
||||
%\qquad -- (Spezifikation u. Datenblätter der verwendeten Messgeräte und/oder Komponenten) \\ % chktex 8
|
||||
%\qquad -- (Berechnungen, Messwerte, Simulationsresultate) \\ % chktex 8
|
||||
%\qquad -- (Stoffdaten) \\ % chktex 8
|
||||
%\qquad -- (Fehlerrechnungen mit Messunsicherheiten) \\ % chktex 8
|
||||
%\qquad -- (Grafische Darstellungen, Fotos) \\ % chktex 8
|
||||
%\qquad -- (Datenträger mit weiteren Daten (z. B. Software-Komponenten) inkl. Verzeichnis der auf diesem Datenträger abgelegten Dateien) \\ % chktex 8
|
||||
%\qquad -- (Softwarecode) % chktex 8
|
||||
%}
|
||||
|
||||
\end{appendices}
|
||||
|
||||
\end{document}
|
@ -0,0 +1,29 @@
|
||||
# Delim
|
||||
|
||||
This is a small helper utility for building the latex document.
|
||||
|
||||
It is called by [thesis.tex](../thesis.tex) with the following `newcommand`:
|
||||
|
||||
```tex
|
||||
\newcommand{\gofilerange}[4][]{%
|
||||
\immediate\write18{./utils/delim -file="#2" -start="#3" -end="#4"}
|
||||
\IfFileExists{code.lineno}
|
||||
{\CatchFileEdef{\linenumber}{./code.lineno}{\endlinechar=-1 }}
|
||||
{\def\linenumber{0}}
|
||||
\edef\flags{firstnumber=\linenumber,#1}
|
||||
\expandafter\gofile\expandafter[\flags]{./code.snippet}
|
||||
}
|
||||
```
|
||||
|
||||
Basically, `delim` receives the filename and a start- and end-comment.
|
||||
It then writes the files content between the two comments into `code.snippet`,
|
||||
and the starting line number into `code.lineno`.
|
||||
|
||||
These are then used by the `minted` package to display the code section
|
||||
within the document.
|
||||
|
||||
Use it in the document like so:
|
||||
|
||||
```tex
|
||||
\gofilerange{this/file.go}{start-section}{end-section}
|
||||
```
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
module github.zhaw.ch/ruettram/bachelor/thesis/utils
|
||||
|
||||
go 1.14
|
@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
targetFile = "code"
|
||||
snippetEnding = ".snippet"
|
||||
lineNumberEnding = ".lineno"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := flag.String("start", "", "the starting delimiter, uncommented")
|
||||
end := flag.String("end", "", "the ending delimiter, uncommented")
|
||||
sourceFile := flag.String("file", "", "the source file")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// this could be parameterised at some point
|
||||
*start = "// " + *start
|
||||
*end = "// " + *end
|
||||
|
||||
file, err := os.Open(*sourceFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
codeDest, err := os.Create(targetFile + snippetEnding)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer codeDest.Close()
|
||||
|
||||
lineDest, err := os.Create(targetFile + lineNumberEnding)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer lineDest.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
// find start
|
||||
for lineno := gotoStart(scanner, *start); lineno != -1; lineno = gotoStart(scanner, *start) {
|
||||
// write line number
|
||||
_, _ = lineDest.WriteString(strconv.Itoa(lineno))
|
||||
// write source cod
|
||||
_, _ = codeDest.WriteString(strings.ReplaceAll(scanner.Text(), *start, "// ...") + "\n")
|
||||
|
||||
// scan until end and write
|
||||
for scanner.Scan() {
|
||||
if strings.TrimSpace(scanner.Text()) == *end {
|
||||
_, _ = codeDest.WriteString(strings.ReplaceAll(scanner.Text(), *end, "// ...") + "\n")
|
||||
break
|
||||
}
|
||||
_, _ = codeDest.WriteString(scanner.Text() + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_ = codeDest.Sync()
|
||||
_ = lineDest.Sync()
|
||||
}
|
||||
|
||||
func gotoStart(s *bufio.Scanner, start string) (linenumber int) {
|
||||
for s.Scan() {
|
||||
if strings.TrimSpace(s.Text()) == start {
|
||||
return linenumber
|
||||
}
|
||||
linenumber++
|
||||
}
|
||||
return -1
|
||||
}
|
@ -0,0 +1,252 @@
|
||||
%%
|
||||
%% This is file `zhawthesis.cls',
|
||||
%% generated with the docstrip utility.
|
||||
%%
|
||||
%% The original source files were:
|
||||
%%
|
||||
%% zhawthesis.dtx (with options: `class')
|
||||
%% ----------------------------------------------------------------
|
||||
%% zhawthesis --- A LaTeX class for writing a thesis at ZHAW.
|
||||
%% E-mail: camerden@students.zhaw.ch
|
||||
%% Released under the LaTeX Project Public License v1.3c or later
|
||||
%% See http://www.latex-project.org/lppl.txt
|
||||
%% ----------------------------------------------------------------
|
||||
%%
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesClass{zhawthesis}[2019/02/28 v1.0 Initial version]
|
||||
%% ========================================================================== %%
|
||||
%% Thesis class %%
|
||||
%% ========================================================================== %%
|
||||
|
||||
\def\zhaw@babellangde{nswissgerman}
|
||||
\def\zhaw@babellangen{english}
|
||||
|
||||
\newcommand{\zhaw@lang}{de}
|
||||
\newcommand{\zhaw@babellang}{\zhaw@langde}
|
||||
\newif\if@german\@germantrue
|
||||
|
||||
\newcommand{\zhaw@thesistype}{}
|
||||
\newcommand{\zhaw@thesistypelong}{}
|
||||
|
||||
\DeclareOption{ba}{
|
||||
\renewcommand{\zhaw@thesistype}{BA}
|
||||
\renewcommand{\zhaw@thesistypelong}{%
|
||||
\if@german{Bachelorarbeit}\else{Bachelor thesis}\fi%
|
||||
}
|
||||
}
|
||||
\DeclareOption{pa}{
|
||||
\renewcommand{\zhaw@thesistype}{PA}
|
||||
\renewcommand{\zhaw@thesistypelong}{%
|
||||
\if@german{Projektarbeit}\else{Project work}\fi%
|
||||
}
|
||||
}
|
||||
|
||||
\DeclareOption{german}{
|
||||
\@germantrue
|
||||
\renewcommand{\zhaw@lang}{de}
|
||||
\renewcommand{\zhaw@babellang}{\zhaw@babellangde}
|
||||
\PassOptionsToClass{\zhaw@babellang}{scrbook}
|
||||
}
|
||||
\DeclareOption{english}{
|
||||
\@germanfalse
|
||||
\renewcommand{\zhaw@lang}{en}
|
||||
\renewcommand{\zhaw@babellang}{\zhaw@babellangen}
|
||||
\PassOptionsToClass{\zhaw@babellang}{scrbook}
|
||||
}
|
||||
|
||||
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{scrbook}}
|
||||
\ProcessOptions
|
||||
|
||||
\RequirePackage[T1]{fontenc}
|
||||
\RequirePackage[utf8]{inputenc}
|
||||
|
||||
\LoadClass[listof=totoc,bibliography=totoc]{scrbook}
|
||||
|
||||
\RequirePackage{microtype}
|
||||
\RequirePackage{graphicx}
|
||||
|
||||
\if@german
|
||||
\RequirePackage[\zhaw@babellangen,main=\zhaw@babellangde]{babel}
|
||||
\else
|
||||
\RequirePackage[\zhaw@babellangde,main=\zhaw@babellangen]{babel}
|
||||
\fi
|
||||
\babeltags{de=\zhaw@babellangde,en=\zhaw@babellangen}
|
||||
\RequirePackage{iflang}
|
||||
|
||||
\RequirePackage[autostyle=try,strict=true,german=swiss,english=british]{csquotes}
|
||||
|
||||
\RequirePackage[output-decimal-marker={.},group-separator={'}]{siunitx}
|
||||
\sisetup{detect-all}
|
||||
|
||||
\RequirePackage[pdfpagelabels]{hyperref}
|
||||
\hypersetup{%
|
||||
unicode,
|
||||
pdfauthor={\@author},
|
||||
pdftitle={\@title},
|
||||
pdfsubject={\zhaw@thesistypelong},
|
||||
pdfkeywords={Thesis;LaTeX}, % TODO: Complete
|
||||
pdfcreator={pdfLaTeX},
|
||||
pdfduplex={DuplexFlipLongEdge},
|
||||
pdflang={\zhaw@lang},
|
||||
bookmarksopen,
|
||||
bookmarksnumbered
|
||||
}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\RequirePackage[automark]{scrlayer-scrpage}
|
||||
\automark[chapter]{chapter}
|
||||
\clearpairofpagestyles
|
||||
\lehead{\@title}
|
||||
\rohead{\@author}
|
||||
\ofoot[\pagemark]{\pagemark}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\newcommand{\zhaw@institute}{}
|
||||
\providecommand{\zhawinstitute}[1]{\renewcommand{\zhaw@institute}{#1}}
|
||||
|
||||
\newcommand{\zhaw@logocolour}{pantone2945}
|
||||
\providecommand{\zhawlogocolour}[1]{\renewcommand{\zhaw@logocolour}{#1}}
|
||||
|
||||
\newcommand{\zhaw@major}{}
|
||||
\providecommand{\major}[1]{\renewcommand{\zhaw@major}{#1}}
|
||||
|
||||
\newcommand{\zhaw@semester}{}
|
||||
\providecommand{\zhawsemester}[1]{\renewcommand{\zhaw@semester}{#1}}
|
||||
|
||||
\newcommand{\zhaw@mainsupervisor}{}
|
||||
\providecommand{\mainsupervisor}[1]{\renewcommand{\zhaw@mainsupervisor}{#1}}
|
||||
|
||||
\newcommand{\zhaw@subsupervisor}{}
|
||||
\providecommand{\subsupervisor}[1]{\renewcommand{\zhaw@subsupervisor}{#1}}
|
||||
|
||||
\newcommand\zhaw@industrypartner{}
|
||||
\providecommand{\industrypartner}[1]{\renewcommand{\zhaw@industrypartner}{#1}}
|
||||
|
||||
\newcommand{\zhaw@extsupervisor}{}
|
||||
\providecommand{\externalsupervisor}[1]{\renewcommand{\zhaw@extsupervisor}{#1}}
|
||||
|
||||
\newcommand{\zhaw@titlepagerow}[2]{%
|
||||
\begin{minipage}[t]{0.3\textwidth}
|
||||
\hrule\vskip 5mm
|
||||
\textbf{#1}
|
||||
\end{minipage}
|
||||
% \hskip 0.03\textwidth
|
||||
\hfil
|
||||
\begin{minipage}[t]{0.67\textwidth}
|
||||
\hrule\vskip 5mm
|
||||
#2 \\
|
||||
\end{minipage}%
|
||||
}
|
||||
|
||||
\newcommand{\zhaw@maketitlepage}{
|
||||
\thispagestyle{empty}
|
||||
\clearpage
|
||||
\begin{titlepage}
|
||||
\sffamily
|
||||
% Logo
|
||||
\begin{picture}(0,0)
|
||||
\put(-30,-50){
|
||||
\includegraphics[width=84.2mm]{logos/zhaw/\zhaw@lang-zhaw-\zhaw@institute-\zhaw@logocolour}
|
||||
}
|
||||
\end{picture}
|
||||
|
||||
\vskip 10mm
|
||||
\hskip 26mm
|
||||
\begin{minipage}[b]{0.91\textwidth}
|
||||
\vskip 20mm
|
||||
{\huge
|
||||
% Projekt Name (max. 2 Zeilen)
|
||||
\textbf{\underline{{\zhaw@thesistypelong} in {\zhaw@major}}} \\
|
||||
\textbf{\underline{{\zhaw@semester}}} \\
|
||||
|
||||
% Projekt Titel (max. 4 Zeilen)
|
||||
{
|
||||
\huge{\@title} \\[1.25ex]
|
||||
\large{\@subtitle}
|
||||
}
|
||||
\vspace{9mm}
|
||||
}
|
||||
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Autoren}{Author}}{%
|
||||
\@author \\
|
||||
}
|
||||
|
||||
\if\zhaw@mainsupervisor\empty\else
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Hauptbetreuung}{Main supervisor}}{%
|
||||
\zhaw@mainsupervisor \\
|
||||
}
|
||||
\fi
|
||||
|
||||
\if\zhaw@subsupervisor\empty\else
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Nebenbetreuung}{Sub supervisor}}{%
|
||||
\zhaw@subsupervisor \\
|
||||
}
|
||||
\fi
|
||||
|
||||
\if\zhaw@industrypartner\empty\else
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Industriepartner}{Industrial partner}}{%
|
||||
\zhaw@industrypartner \\
|
||||
}
|
||||
\fi
|
||||
|
||||
\if\zhaw@extsupervisor\empty\else
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Externe Betreuung}{External supervisor}}{%
|
||||
\zhaw@extsupervisor \\
|
||||
}
|
||||
\fi
|
||||
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Datum}{Date}}{%
|
||||
\@date \\
|
||||
}
|
||||
\end{minipage}
|
||||
\vskip 5mm
|
||||
\end{titlepage}
|
||||
}
|
||||
\renewcommand{\maketitle}{\zhaw@maketitlepage}
|
||||
|
||||
\RequirePackage{pdfpages}
|
||||
\providecommand{\makedeclarationoforiginality}{%
|
||||
\cleardoublepage%
|
||||
\if@german%
|
||||
\includepdf{includes/Erklaerung_Selbstaendigkeit_SoE_\zhaw@thesistype_de.pdf}
|
||||
\else%
|
||||
\includepdf{includes/Declaration_of_Originality_SoE_\zhaw@thesistype_en.pdf}
|
||||
\fi%
|
||||
}
|
||||
|
||||
\AtBeginDocument{%
|
||||
\pagestyle{scrheadings}
|
||||
|
||||
% Set 1st level itemize bullet symbol to --
|
||||
\def\labelitemi{--}
|
||||
}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\providecommand\norm[1]{\left\lVert#1\right\rVert}
|
||||
|
||||
\def\signed #1{{\leavevmode\unskip\nobreak\hfil\penalty50\hskip2em
|
||||
\hbox{}\nobreak\hfil(#1)%
|
||||
\parfillskip=0pt \finalhyphendemerits=0 \endgraf}}
|
||||
|
||||
%%
|
||||
%% Copyright (C) 2019 by Dennis Camera <camerden@students.zhaw.ch>
|
||||
%%
|
||||
%% This work may be distributed and/or modified under the
|
||||
%% conditions of the LaTeX Project Public License (LPPL), either
|
||||
%% version 1.3c of this license or (at your option) any later
|
||||
%% version. The latest version of this license is in the file:
|
||||
%%
|
||||
%% http://www.latex-project.org/lppl.txt
|
||||
%%
|
||||
%% This work is "maintained" (as per LPPL maintenance status).
|
||||
%%
|
||||
%% This work consists of the file zhawthesis.dtx
|
||||
%% and the derived files zhawthesis.ins,
|
||||
%% zhawthesis.pdf and
|
||||
%% zhawthesis.cls.
|
||||
%%
|
||||
%%
|
||||
%% End of file `zhawthesis.cls'.
|
@ -0,0 +1,362 @@
|
||||
% \iffalse meta-comment
|
||||
% !TEX program = pdfLaTeX
|
||||
%<*internal>
|
||||
\iffalse
|
||||
%</internal>
|
||||
%<*readme>
|
||||
----------------------------------------------------------------
|
||||
zhawthesis --- A LaTeX class for writing a thesis at ZHAW.
|
||||
E-mail: camerden@students.zhaw.ch
|
||||
Released under the LaTeX Project Public License v1.3c or later
|
||||
See http://www.latex-project.org/lppl.txt
|
||||
----------------------------------------------------------------
|
||||
|
||||
The zhawthesis class provides a framework to build your thesis
|
||||
around.
|
||||
|
||||
Please note: zhawthesis is not officially endorsed by the
|
||||
ZHAW Zurich University of Applied Sciences!
|
||||
%</readme>
|
||||
%<*internal>
|
||||
\fi
|
||||
\def\nameofplainTeX{plain}
|
||||
\ifx\fmtname\nameofplainTeX\else
|
||||
\expandafter\begingroup
|
||||
\fi
|
||||
%</internal>
|
||||
%<*install>
|
||||
\input docstrip.tex
|
||||
\keepsilent
|
||||
\askforoverwritefalse
|
||||
\preamble
|
||||
----------------------------------------------------------------
|
||||
zhawthesis --- A LaTeX class for writing a thesis at ZHAW.
|
||||
E-mail: camerden@students.zhaw.ch
|
||||
Released under the LaTeX Project Public License v1.3c or later
|
||||
See http://www.latex-project.org/lppl.txt
|
||||
----------------------------------------------------------------
|
||||
|
||||
\endpreamble
|
||||
\postamble
|
||||
|
||||
Copyright (C) 2019 by Dennis Camera <camerden@students.zhaw.ch>
|
||||
|
||||
This work may be distributed and/or modified under the
|
||||
conditions of the LaTeX Project Public License (LPPL), either
|
||||
version 1.3c of this license or (at your option) any later
|
||||
version. The latest version of this license is in the file:
|
||||
|
||||
http://www.latex-project.org/lppl.txt
|
||||
|
||||
This work is "maintained" (as per LPPL maintenance status).
|
||||
|
||||
This work consists of the file zhawthesis.dtx
|
||||
and the derived files zhawthesis.ins,
|
||||
zhawthesis.pdf and
|
||||
zhawthesis.cls.
|
||||
|
||||
\endpostamble
|
||||
\usedir{tex/latex/zhawthesis}
|
||||
\generate{
|
||||
\file{\jobname.cls}{\from{\jobname.dtx}{class}}
|
||||
}
|
||||
%</install>
|
||||
%<install>\endbatchfile
|
||||
%<*internal>
|
||||
\usedir{source/latex/zhawthesis}
|
||||
\generate{
|
||||
\file{\jobname.ins}{\from{\jobname.dtx}{install}}
|
||||
}
|
||||
\nopreamble\nopostamble
|
||||
\usedir{doc/latex/zhawthesis}
|
||||
% XXX: Do not generate README.txt since we already have a README.md in this repository
|
||||
% \generate{
|
||||
% \file{README.txt}{\from{\jobname.dtx}{readme}}
|
||||
% }
|
||||
\ifx\fmtname\nameofplainTeX
|
||||
\expandafter\endbatchfile
|
||||
\else
|
||||
\expandafter\endgroup
|
||||
\fi
|
||||
%</internal>
|
||||
%<*class>
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesClass{zhawthesis}[2019/02/28 v1.0 Initial version]
|
||||
%</class>
|
||||
%<*driver>
|
||||
\documentclass{ltxdoc}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[numbered]{hypdoc}
|
||||
\EnableCrossrefs
|
||||
\CodelineIndex
|
||||
\RecordChanges
|
||||
\OnlyDescription
|
||||
\usepackage{microtype}
|
||||
\begin{document}
|
||||
\DocInput{\jobname.dtx}
|
||||
\end{document}
|
||||
%</driver>
|
||||
% \fi
|
||||
%
|
||||
%\GetFileInfo{\jobname.cls}
|
||||
%
|
||||
%\title{^^A
|
||||
% \textsf{zhawthesis} --- ZHAW thesis\thanks{^^A
|
||||
% This file describes version \fileversion, last revised \filedate.^^A
|
||||
% }^^A
|
||||
%}
|
||||
%\author{^^A
|
||||
% Dennis Camera\thanks{E-mail: camerden@students.zhaw.ch}^^A
|
||||
%}
|
||||
%\date{Released \filedate}
|
||||
%
|
||||
%\maketitle
|
||||
%
|
||||
%\changes{v1.0}{2019/02/28}{First public release}
|
||||
%
|
||||
%\StopEventually{^^A
|
||||
% \clearpage
|
||||
% \PrintChanges
|
||||
% \PrintIndex
|
||||
%}
|
||||
%
|
||||
%\section*{The Code}
|
||||
% \begin{macrocode}
|
||||
%<*class>
|
||||
% \end{macrocode}
|
||||
%% ========================================================================== %%
|
||||
%% Thesis class %%
|
||||
%% ========================================================================== %%
|
||||
|
||||
|
||||
\def\zhaw@babellangde{nswissgerman}
|
||||
\def\zhaw@babellangen{english}
|
||||
|
||||
\newcommand{\zhaw@lang}{de}
|
||||
\newcommand{\zhaw@babellang}{\zhaw@langde}
|
||||
\newif\if@german\@germantrue
|
||||
|
||||
\newcommand{\zhaw@thesistype}{}
|
||||
\newcommand{\zhaw@thesistypelong}{}
|
||||
|
||||
\DeclareOption{ba}{
|
||||
\renewcommand{\zhaw@thesistype}{BA}
|
||||
\renewcommand{\zhaw@thesistypelong}{%
|
||||
\if@german{Bachelorarbeit}\else{Bachelor thesis}\fi%
|
||||
}
|
||||
}
|
||||
\DeclareOption{pa}{
|
||||
\renewcommand{\zhaw@thesistype}{PA}
|
||||
\renewcommand{\zhaw@thesistypelong}{%
|
||||
\if@german{Projektarbeit}\else{Project work}\fi%
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
\DeclareOption{german}{
|
||||
\@germantrue
|
||||
\renewcommand{\zhaw@lang}{de}
|
||||
\renewcommand{\zhaw@babellang}{\zhaw@babellangde}
|
||||
\PassOptionsToClass{\zhaw@babellang}{scrbook}
|
||||
}
|
||||
\DeclareOption{english}{
|
||||
\@germanfalse
|
||||
\renewcommand{\zhaw@lang}{en}
|
||||
\renewcommand{\zhaw@babellang}{\zhaw@babellangen}
|
||||
\PassOptionsToClass{\zhaw@babellang}{scrbook}
|
||||
}
|
||||
|
||||
|
||||
% Passes and class options to the underlying article class
|
||||
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{scrbook}}
|
||||
\ProcessOptions
|
||||
|
||||
\RequirePackage[T1]{fontenc}
|
||||
\RequirePackage[utf8]{inputenc}
|
||||
|
||||
\LoadClass[listof=totoc,bibliography=totoc]{scrbook}
|
||||
|
||||
\RequirePackage{microtype}
|
||||
\RequirePackage{graphicx}
|
||||
|
||||
\if@german
|
||||
\RequirePackage[\zhaw@babellangen,main=\zhaw@babellangde]{babel}
|
||||
\else
|
||||
\RequirePackage[\zhaw@babellangde,main=\zhaw@babellangen]{babel}
|
||||
\fi
|
||||
\babeltags{de=\zhaw@babellangde,en=\zhaw@babellangen}
|
||||
\RequirePackage{iflang}
|
||||
|
||||
\RequirePackage[autostyle=try,strict=true,german=swiss,english=british]{csquotes}
|
||||
|
||||
\RequirePackage[output-decimal-marker={.},group-separator={'}]{siunitx}
|
||||
\sisetup{detect-all}
|
||||
|
||||
|
||||
% hyperref
|
||||
\RequirePackage[pdfpagelabels]{hyperref}
|
||||
\hypersetup{%
|
||||
unicode,
|
||||
pdfauthor={\@author},
|
||||
pdftitle={\@title},
|
||||
pdfsubject={\zhaw@thesistypelong},
|
||||
pdfkeywords={Thesis;LaTeX}, % TODO: Complete
|
||||
pdfcreator={pdfLaTeX},
|
||||
pdfduplex={DuplexFlipLongEdge},
|
||||
pdflang={\zhaw@lang},
|
||||
bookmarksopen,
|
||||
bookmarksnumbered
|
||||
}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Configure header and footers
|
||||
|
||||
\RequirePackage[automark]{scrlayer-scrpage}
|
||||
\automark[chapter]{chapter}
|
||||
\clearpairofpagestyles
|
||||
\lehead{\@title}
|
||||
\rohead{\@author}
|
||||
\ofoot[\pagemark]{\pagemark}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% ZHAW functions
|
||||
|
||||
\newcommand{\zhaw@institute}{}
|
||||
\providecommand{\zhawinstitute}[1]{\renewcommand{\zhaw@institute}{#1}}
|
||||
|
||||
\newcommand{\zhaw@logocolour}{pantone2945}
|
||||
\providecommand{\zhawlogocolour}[1]{\renewcommand{\zhaw@logocolour}{#1}}
|
||||
|
||||
\newcommand{\zhaw@major}{}
|
||||
\providecommand{\major}[1]{\renewcommand{\zhaw@major}{#1}}
|
||||
|
||||
\newcommand{\zhaw@semester}{}
|
||||
\providecommand{\zhawsemester}[1]{\renewcommand{\zhaw@semester}{#1}}
|
||||
|
||||
\newcommand{\zhaw@mainsupervisor}{}
|
||||
\providecommand{\mainsupervisor}[1]{\renewcommand{\zhaw@mainsupervisor}{#1}}
|
||||
|
||||
\newcommand{\zhaw@subsupervisor}{}
|
||||
\providecommand{\subsupervisor}[1]{\renewcommand{\zhaw@subsupervisor}{#1}}
|
||||
|
||||
\newcommand\zhaw@industrypartner{}
|
||||
\providecommand{\industrypartner}[1]{\renewcommand{\zhaw@industrypartner}{#1}}
|
||||
|
||||
\newcommand{\zhaw@extsupervisor}{}
|
||||
\providecommand{\externalsupervisor}[1]{\renewcommand{\zhaw@extsupervisor}{#1}}
|
||||
|
||||
|
||||
\newcommand{\zhaw@titlepagerow}[2]{%
|
||||
\begin{minipage}[t]{0.3\textwidth}
|
||||
\hrule\vskip 5mm
|
||||
\textbf{#1}
|
||||
\end{minipage}
|
||||
% \hskip 0.03\textwidth
|
||||
\hfil
|
||||
\begin{minipage}[t]{0.67\textwidth}
|
||||
\hrule\vskip 5mm
|
||||
#2 \\
|
||||
\end{minipage}%
|
||||
}
|
||||
|
||||
\newcommand{\zhaw@maketitlepage}{
|
||||
\thispagestyle{empty}
|
||||
\clearpage
|
||||
\begin{titlepage}
|
||||
\sffamily
|
||||
% Logo
|
||||
\begin{picture}(0,0)
|
||||
\put(-30,-50){
|
||||
\includegraphics[width=84.2mm]{logos/zhaw/\zhaw@lang-zhaw-\zhaw@institute-\zhaw@logocolour}
|
||||
}
|
||||
\end{picture}
|
||||
|
||||
\vskip 10mm
|
||||
\hskip 26mm
|
||||
\begin{minipage}[b]{0.91\textwidth}
|
||||
\vskip 20mm
|
||||
{\huge
|
||||
% Projekt Name (max. 2 Zeilen)
|
||||
\textbf{\underline{{\zhaw@thesistypelong} in {\zhaw@major}}} \\
|
||||
\textbf{\underline{{\zhaw@semester}}} \\
|
||||
|
||||
% Projekt Titel (max. 4 Zeilen)
|
||||
{
|
||||
\huge{\@title} \\[1.25ex]
|
||||
\large{\@subtitle}
|
||||
}
|
||||
\vspace{9mm}
|
||||
}
|
||||
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Autoren}{Author}}{%
|
||||
\@author \\
|
||||
}
|
||||
|
||||
\if\zhaw@mainsupervisor\empty\else
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Hauptbetreuung}{Main supervisor}}{%
|
||||
\zhaw@mainsupervisor \\
|
||||
}
|
||||
\fi
|
||||
|
||||
\if\zhaw@subsupervisor\empty\else
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Nebenbetreuung}{Sub supervisor}}{%
|
||||
\zhaw@subsupervisor \\
|
||||
}
|
||||
\fi
|
||||
|
||||
\if\zhaw@industrypartner\empty\else
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Industriepartner}{Industrial partner}}{%
|
||||
\zhaw@industrypartner \\
|
||||
}
|
||||
\fi
|
||||
|
||||
\if\zhaw@extsupervisor\empty\else
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Externe Betreuung}{External supervisor}}{%
|
||||
\zhaw@extsupervisor \\
|
||||
}
|
||||
\fi
|
||||
|
||||
\zhaw@titlepagerow{\IfLanguageName{nswissgerman}{Datum}{Date}}{%
|
||||
\@date \\
|
||||
}
|
||||
\end{minipage}
|
||||
\vskip 5mm
|
||||
\end{titlepage}
|
||||
}
|
||||
\renewcommand{\maketitle}{\zhaw@maketitlepage}
|
||||
|
||||
\RequirePackage{pdfpages}
|
||||
\providecommand{\makedeclarationoforiginality}{%
|
||||
\cleardoublepage%
|
||||
\if@german%
|
||||
\includepdf{includes/Erklaerung_Selbstaendigkeit_SoE_\zhaw@thesistype_de.pdf}
|
||||
\else%
|
||||
\includepdf{includes/Declaration_of_Originality_SoE_\zhaw@thesistype_en.pdf}
|
||||
\fi%
|
||||
}
|
||||
|
||||
|
||||
\AtBeginDocument{%
|
||||
\pagestyle{scrheadings}
|
||||
|
||||
% Set 1st level itemize bullet symbol to --
|
||||
\def\labelitemi{--}
|
||||
}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Functions
|
||||
|
||||
% Define math operators
|
||||
\providecommand\norm[1]{\left\lVert#1\right\rVert}
|
||||
|
||||
% \signed{...} function for quote blocks
|
||||
\def\signed #1{{\leavevmode\unskip\nobreak\hfil\penalty50\hskip2em
|
||||
\hbox{}\nobreak\hfil(#1)%
|
||||
\parfillskip=0pt \finalhyphendemerits=0 \endgraf}}
|
||||
|
||||
% \begin{macrocode}
|
||||
%</class>
|
||||
% \end{macrocode}
|
||||
%\Finale
|
@ -0,0 +1,69 @@
|
||||
%%
|
||||
%% This is file `zhawthesis.ins',
|
||||
%% generated with the docstrip utility.
|
||||
%%
|
||||
%% The original source files were:
|
||||
%%
|
||||
%% zhawthesis.dtx (with options: `install')
|
||||
%% ----------------------------------------------------------------
|
||||
%% zhawthesis --- A LaTeX class for writing a thesis at ZHAW.
|
||||
%% E-mail: camerden@students.zhaw.ch
|
||||
%% Released under the LaTeX Project Public License v1.3c or later
|
||||
%% See http://www.latex-project.org/lppl.txt
|
||||
%% ----------------------------------------------------------------
|
||||
%%
|
||||
\input docstrip.tex
|
||||
\keepsilent
|
||||
\askforoverwritefalse
|
||||
\preamble
|
||||
----------------------------------------------------------------
|
||||
zhawthesis --- A LaTeX class for writing a thesis at ZHAW.
|
||||
E-mail: camerden@students.zhaw.ch
|
||||
Released under the LaTeX Project Public License v1.3c or later
|
||||
See http://www.latex-project.org/lppl.txt
|
||||
----------------------------------------------------------------
|
||||
|
||||
\endpreamble
|
||||
\postamble
|
||||
|
||||
Copyright (C) 2019 by Dennis Camera <camerden@students.zhaw.ch>
|
||||
|
||||
This work may be distributed and/or modified under the
|
||||
conditions of the LaTeX Project Public License (LPPL), either
|
||||
version 1.3c of this license or (at your option) any later
|
||||
version. The latest version of this license is in the file:
|
||||
|
||||
http://www.latex-project.org/lppl.txt
|
||||
|
||||
This work is "maintained" (as per LPPL maintenance status).
|
||||
|
||||
This work consists of the file zhawthesis.dtx
|
||||
and the derived files zhawthesis.ins,
|
||||
zhawthesis.pdf and
|
||||
zhawthesis.cls.
|
||||
|
||||
\endpostamble
|
||||
\usedir{tex/latex/zhawthesis}
|
||||
\generate{
|
||||
\file{\jobname.cls}{\from{\jobname.dtx}{class}}
|
||||
}
|
||||
\endbatchfile
|
||||
%%
|
||||
%% Copyright (C) 2019 by Dennis Camera <camerden@students.zhaw.ch>
|
||||
%%
|
||||
%% This work may be distributed and/or modified under the
|
||||
%% conditions of the LaTeX Project Public License (LPPL), either
|
||||
%% version 1.3c of this license or (at your option) any later
|
||||
%% version. The latest version of this license is in the file:
|
||||
%%
|
||||
%% http://www.latex-project.org/lppl.txt
|
||||
%%
|
||||
%% This work is "maintained" (as per LPPL maintenance status).
|
||||
%%
|
||||
%% This work consists of the file zhawthesis.dtx
|
||||
%% and the derived files zhawthesis.ins,
|
||||
%% zhawthesis.pdf and
|
||||
%% zhawthesis.cls.
|
||||
%%
|
||||
%%
|
||||
%% End of file `zhawthesis.ins'.
|
Binary file not shown.
@ -0,0 +1 @@
|
||||
Subproject commit 0009512345fbd95fe1c745414fffed6c63ccd1aa
|
@ -0,0 +1 @@
|
||||
Subproject commit 66c72f095e6da255bde8df6a913815a7dde25665
|
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage example:
|
||||
# ./count-function.sh ":" "((map)|(fmap))" "((foldr)|(foldl'?))" "filter" \
|
||||
# "reverse" "take" "drop" "sum" "zip" "product" "maximum" "minimum"
|
||||
|
||||
|
||||
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
echo "Searching for occurrences in subdirectories of $dir"
|
||||
for func in "$@"; do
|
||||
occurrences="$(rg --type hs " $func " --count-matches | awk -F ':' '{sum += $2} END {print sum}')"
|
||||
echo "Found $occurrences occurrences of \"$func\""
|
||||
done
|
@ -0,0 +1 @@
|
||||
Subproject commit 91f2bcfe73fa3a489654ee74bca02e24423dc5c0
|
@ -0,0 +1 @@
|
||||
Subproject commit dea57bd1bec9ba5c4e438de80b2017eee4a30a40
|
@ -0,0 +1 @@
|
||||
Subproject commit 183fc22549011804d973e01654e354b728f2bc70
|
@ -0,0 +1 @@
|
||||
Subproject commit 124b45d36d7f81e45a9aa3aef588fd5e132fb6fd
|
@ -0,0 +1 @@
|
||||
Subproject commit 68a03e05e5e030d7274c712ffa39768310e70f8a
|
@ -0,0 +1,3 @@
|
||||
module newbuiltins
|
||||
|
||||
go 1.14
|
@ -0,0 +1,47 @@
|
||||
|
||||
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Counterpart to Haskell's `derive Show` through code generation
|
||||
//go:generate stringer -type parity
|
||||
|
||||
type parity int
|
||||
|
||||
const even parity = 0
|
||||
const odd parity = 1
|
||||
|
||||
// shouldBe returns a function that returns true if an int is
|
||||
// of the given parity
|
||||
func shouldBe(p parity) func(i int) bool {
|
||||
return func(i int) bool {
|
||||
return i%2 == int(p)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
lst := []int{1, 2, 3, 4, 5}
|
||||
lstMult := fmap(
|
||||
func(i int) int { return i * 5 },
|
||||
prepend(0, lst),
|
||||
)
|
||||
|
||||
addToString := func(s string, i int) string {
|
||||
return s + strconv.Itoa(i) + " "
|
||||
}
|
||||
|
||||
// fold over even / odd numbers and add them to a string
|
||||
evens := foldl(addToString, even.String()+": ",
|
||||
filter(shouldBe(even), lstMult))
|
||||
|
||||
odds := foldl(addToString, odd.String()+": ",
|
||||
filter(shouldBe(odd), lstMult))
|
||||
|
||||
fmt.Println(evens, odds)
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
// Code generated by "stringer -type parity"; DO NOT EDIT.
|
||||
|
||||
package main
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[even-0]
|
||||
_ = x[odd-1]
|
||||
}
|
||||
|
||||
const _parity_name = "evenodd"
|
||||
|
||||
var _parity_index = [...]uint8{0, 4, 7}
|
||||
|
||||
func (i parity) String() string {
|
||||
if i >= parity(len(_parity_index)-1) {
|
||||
return "parity(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _parity_name[_parity_index[i]:_parity_index[i+1]]
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
module foldl-workaround
|
||||
|
||||
go 1.14
|
@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
zero, one, two, three := 0, 1, 2, 3
|
||||
list := []*int{&one, &two, nil, &three, &zero}
|
||||
|
||||
// this will work, as the values will be evaluated
|
||||
// "lazily" - the nested functions will never
|
||||
// be executed, thus it will never panic.
|
||||
fmt.Printf("%v\n", myFold(mulLazy, 1, list))
|
||||
// This will panic.
|
||||
fmt.Printf("%v\n", foldl(mul, 1, list))
|
||||
}
|
||||
|
||||
func mul(x int, y *int) int {
|
||||
if *y == 0 {
|
||||
return 0
|
||||
}
|
||||
return x * *y
|
||||
}
|
||||
|
||||
func mulLazy(x func() int, y *int) func() int {
|
||||
return func() int {
|
||||
if *y == 0 {
|
||||
return 0
|
||||
}
|
||||
return x() * *y
|
||||
}
|
||||
}
|
||||
|
||||
func myFold(f func(func() int, *int) func() int, acc int, list []*int) int {
|
||||
a := func() int { return acc }
|
||||
a = foldl(f, a, list)
|
||||
return a()
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package fractran
|
||||
|
||||
type fraction struct {
|
||||
numerator int
|
||||
denominator int
|
||||
}
|
||||
|
||||
type multResult interface {
|
||||
multResult()
|
||||
}
|
||||
|
||||
type success int
|
||||
|
||||
func (success) multResult() {}
|
||||
|
||||
type failure struct{}
|
||||
|
||||
func (failure) multResult() {}
|
||||
|
||||
func mult(f fraction, n int) multResult {
|
||||
switch (n * f.numerator) % f.denominator {
|
||||
case 0:
|
||||
return success(n * f.numerator / f.denominator)
|
||||
default:
|
||||
return failure{}
|
||||
}
|
||||
}
|
||||
|
||||
type program []fraction
|
||||
|
||||
func exec(p program, input int) []int {
|
||||
var run func([]fraction, []int) []int
|
||||
|
||||
run = func(f []fraction, inputs []int) []int {
|
||||
if len(f) == 0 {
|
||||
return inputs
|
||||
}
|
||||
switch r := mult(f[0], inputs[0]).(type) {
|
||||
case success:
|
||||
return run(p, prepend(int(r), inputs))
|
||||
case failure:
|
||||
return run(f[1:], inputs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return run(p, []int{input})
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package fractran
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFractran(t *testing.T) {
|
||||
p := program{
|
||||
{91, 33},
|
||||
{11, 13},
|
||||
{1, 11},
|
||||
{399, 34},
|
||||
{17, 19},
|
||||
{1, 17},
|
||||
{2, 7},
|
||||
{187, 5},
|
||||
{1, 3},
|
||||
}
|
||||
|
||||
input := 31250
|
||||
|
||||
res := exec(p, input)
|
||||
if res[0] != 8192 {
|
||||
t.Errorf("did not get correct result. expected=%v, got=%v", 8192, res[0])
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
module fractran
|
||||
|
||||
go 1.14
|
@ -0,0 +1,3 @@
|
||||
module functional-options
|
||||
|
||||
go 1.14
|
@ -0,0 +1,32 @@
|
||||
package webserver
|
||||
|
||||
type Server struct {
|
||||
Timeout time.Duration
|
||||
Port int
|
||||
ListenAddress string
|
||||
}
|
||||
type Option func(*Server)
|
||||
|
||||
func Timeout(d time.Duration) Option {
|
||||
return func(s *Server) { s.Timeout = d }
|
||||
}
|
||||
func Port(p int) Option {
|
||||
return func(s *Server) { s.Port = p }
|
||||
}
|
||||
|
||||
func New(opts ...Option) *Server {
|
||||
s := &Server{ // initialise with default values
|
||||
Timeout: 500*time.Millisecond,
|
||||
Port: 0, // uses a random port on the host
|
||||
ListenAddress: "http://localhost",
|
||||
}
|
||||
// apply all options
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// usage examples from outside the package:
|
||||
s := webserver.New() // uses all the default options
|
||||
s := webserver.New(webserver.Timeout(time.Second), webserver.Port(8080))
|
@ -0,0 +1,3 @@
|
||||
module funcsort
|
||||
|
||||
go 1.14
|
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println(quicksort([]int{1, 8, 5, 3, 4, 9}))
|
||||
}
|
||||
|
||||
// start-quicksort
|
||||
func quicksort(p []int) []int {
|
||||
if len(p) == 0 {
|
||||
return []int{}
|
||||
}
|
||||
|
||||
lesser := filter(func(x int) bool { return p[0] > x }, p[1:])
|
||||
greater := filter(func(x int) bool { return p[0] <= x }, p[1:])
|
||||
|
||||
return append(quicksort(lesser), prepend(p[0], quicksort(greater))...)
|
||||
}
|
||||
// end-quicksort
|
@ -0,0 +1,3 @@
|
||||
module intlist
|
||||
|
||||
go 1.14
|
@ -0,0 +1,41 @@
|
||||
package list
|
||||
|
||||
func listOps(list []int) {
|
||||
// collect all elements in a list and increment
|
||||
// each element by one
|
||||
var incremented []int
|
||||
for _, n := range list {
|
||||
incremented = append(incremented, n+1)
|
||||
}
|
||||
|
||||
// same, but with new builtin
|
||||
incremented2 := fmap(
|
||||
func(x int) int { return x + 1 },
|
||||
list)
|
||||
|
||||
// scale each element by factor s
|
||||
// and add t, collect results in
|
||||
// new list
|
||||
s := 2
|
||||
t := 1
|
||||
scaled := fmap(
|
||||
func(x int) int { return s*x + t },
|
||||
list)
|
||||
|
||||
// filter all even elements and
|
||||
// collect them in a new list
|
||||
even := filter(
|
||||
func(x int) bool { return x%2 == 0 },
|
||||
list)
|
||||
|
||||
// sum all elements in the list
|
||||
sum_reduce := 0
|
||||
for _, n := range list {
|
||||
sum_reduce += n
|
||||
}
|
||||
|
||||
// same, but with builtin
|
||||
sum_reduce2 := foldl(
|
||||
func(a, b int) int { return a + b },
|
||||
0, list)
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
public static void listLambdas(List<Integer> list) {
|
||||
// Sammelt alle Elemente der Liste um 1 inkrementiert
|
||||
// in einer neuen Liste
|
||||
List <Integer > incremented = new ArrayList<Integer>();
|
||||
for(int n : list) {
|
||||
incremented.add(n+1);
|
||||
}
|
||||
|
||||
// Variante mit Lambdas
|
||||
List<Integer> incremented2 = list.stream()
|
||||
.map(x -> x + 1)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Skaliert die Element in der Liste um den Faktor s
|
||||
// und addiert dazu jeweils t;
|
||||
// sammelt das Ergebnis in einer neuen Liste
|
||||
final int s = 2;
|
||||
final int t = 1;
|
||||
List<Integer> scaled = list.stream()
|
||||
.map(x -> s * x + t)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Filtert alle geraden Elemente aus der Liste
|
||||
// und sammelt sie in einer neuen Liste
|
||||
List<Integer> even = list.stream()
|
||||
.filter(x -> x % 2 == 0)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Summiert die Elemente in der Liste
|
||||
int summe_reduce = 0;
|
||||
for(int n : list) {
|
||||
summe_reduce += n;
|
||||
}
|
||||
|
||||
// Variante mit Lambdas
|
||||
int summe_reduce2 = list.stream()
|
||||
.reduce(0, (a, b) -> a + b);
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
module mutate
|
||||
|
||||
go 1.14
|
@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
m := MyStruct{
|
||||
x: "struct",
|
||||
y: 42,
|
||||
}
|
||||
|
||||
fmt.Println(m) // {struct 42}
|
||||
mutateNoPointer(m)
|
||||
fmt.Println(m) // {struct 42}
|
||||
mutatePointer(&m)
|
||||
fmt.Println(m) // {changed 0}
|
||||
}
|
||||
|
||||
type MyStruct struct {
|
||||
x string
|
||||
y int
|
||||
}
|
||||
|
||||
func mutateNoPointer(m MyStruct) {
|
||||
m.x = "changed"
|
||||
m.y = 0
|
||||
}
|
||||
func mutatePointer(m *MyStruct) {
|
||||
m.x = "changed"
|
||||
m.y = 0
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
module shadowing
|
||||
|
||||
go 1.14
|
@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
x := 5
|
||||
fmt.Println(x) // 5
|
||||
// introducing a new scope
|
||||
{
|
||||
// this assignment would be forbidden, as it
|
||||
// overwrites the parent block's value value.
|
||||
x = 3
|
||||
fmt.Println(x) // 3
|
||||
}
|
||||
fmt.Println(x) // 3
|
||||
// introducing a new scope
|
||||
{
|
||||
// this redeclares the variable x, effectively
|
||||
// shadowing it. This will not change the parent
|
||||
// block's variable.
|
||||
x := 4
|
||||
fmt.Println(x) // 4
|
||||
}
|
||||
fmt.Println(x) // 3
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
module sumtypes
|
||||
|
||||
go 1.14
|
@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
x := get(false)
|
||||
|
||||
switch m := x.(type) {
|
||||
case myInt:
|
||||
fmt.Printf("Is integer: %v\n", m)
|
||||
case myString:
|
||||
fmt.Printf("Is string: %s\n", m)
|
||||
}
|
||||
}
|
||||
|
||||
func get(b bool) MySumType {
|
||||
if b {
|
||||
return myInt(0)
|
||||
}
|
||||
return myString("zero")
|
||||
}
|
||||
|
||||
type MySumType interface {
|
||||
mysumtype()
|
||||
}
|
||||
|
||||
type myInt int
|
||||
|
||||
func (m myInt) mysumtype() {}
|
||||
|
||||
type myString string
|
||||
|
||||
func (m myString) mysumtype() {}
|
@ -0,0 +1,107 @@
|
||||
package calendar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// this is basically the same as Haskell's "derive Show"
|
||||
//go:generate stringer -type dayOfWeek
|
||||
|
||||
type dayOfWeek int
|
||||
|
||||
const (
|
||||
Sunday dayOfWeek = iota
|
||||
Monday
|
||||
Tuesday
|
||||
Wednesday
|
||||
Thursday
|
||||
Friday
|
||||
Saturday
|
||||
)
|
||||
|
||||
//go:generate stringer -type month
|
||||
|
||||
type month int
|
||||
|
||||
const (
|
||||
January month = iota
|
||||
February
|
||||
March
|
||||
April
|
||||
May
|
||||
June
|
||||
July
|
||||
August
|
||||
September
|
||||
October
|
||||
November
|
||||
December
|
||||
)
|
||||
|
||||
func next(x dayOfWeek) dayOfWeek {
|
||||
switch x {
|
||||
case Saturday: // reset to 0
|
||||
return Sunday
|
||||
default:
|
||||
return dayOfWeek(int(x) + 1)
|
||||
}
|
||||
}
|
||||
|
||||
func pad(n int) string {
|
||||
switch {
|
||||
case n < 10:
|
||||
return " " + strconv.Itoa(n)
|
||||
default:
|
||||
return strconv.Itoa(n)
|
||||
}
|
||||
}
|
||||
|
||||
const weekHeader = "Su Mo Tu We Th Fr Sa"
|
||||
|
||||
func calendarMonth(m month, startDay dayOfWeek, maxDay int) string {
|
||||
var days func(dayOfWeek, int) string
|
||||
days = func(d dayOfWeek, n int) string {
|
||||
switch {
|
||||
case d == Sunday && n > maxDay:
|
||||
return "\n"
|
||||
case n > maxDay:
|
||||
return "\n\n"
|
||||
case d == Saturday:
|
||||
return pad(n) + "\n" + days(Sunday, n+1)
|
||||
default:
|
||||
return pad(n) + " " + days(next(d), n+1)
|
||||
}
|
||||
}
|
||||
// spaces is used to add the initial padding in every month,
|
||||
// depending on the starting day
|
||||
var spaces func(dayOfWeek) string
|
||||
spaces = func(currDay dayOfWeek) string {
|
||||
switch currDay {
|
||||
case startDay:
|
||||
return days(startDay, 1)
|
||||
default:
|
||||
return " " + spaces(next(currDay))
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v 2020\n%v\n%v", m, weekHeader, spaces(Sunday))
|
||||
}
|
||||
|
||||
func Calendar() {
|
||||
year := calendarMonth(January, Wednesday, 31) +
|
||||
calendarMonth(February, Saturday, 29) +
|
||||
calendarMonth(March, Sunday, 31) +
|
||||
calendarMonth(April, Wednesday, 30) +
|
||||
calendarMonth(May, Friday, 31) +
|
||||
calendarMonth(June, Monday, 30) +
|
||||
calendarMonth(July, Wednesday, 31) +
|
||||
calendarMonth(August, Saturday, 31) +
|
||||
calendarMonth(September, Tuesday, 30) +
|
||||
calendarMonth(October, Thursday, 31) +
|
||||
calendarMonth(November, Sunday, 30) +
|
||||
calendarMonth(December, Tuesday, 31)
|
||||
|
||||
fmt.Println(year)
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue