diff options
| author | Cadey Dodrill <me@christine.website> | 2016-12-14 06:20:25 -0800 |
|---|---|---|
| committer | Cadey Dodrill <me@christine.website> | 2016-12-14 06:20:25 -0800 |
| commit | 90f176019a1723aa0d4b7cbb97009944e60e963d (patch) | |
| tree | a816f40b0c8c823aef2c8efe60ba4ef8891d876b | |
| parent | 060a7c913a6eb1f30052504678e04fccc404b930 (diff) | |
| download | xesite-90f176019a1723aa0d4b7cbb97009944e60e963d.tar.xz xesite-90f176019a1723aa0d4b7cbb97009944e60e963d.zip | |
add API backend
19 files changed, 2239 insertions, 7 deletions
diff --git a/backend/christine.website/main.go b/backend/christine.website/main.go new file mode 100644 index 0000000..aafdfa3 --- /dev/null +++ b/backend/christine.website/main.go @@ -0,0 +1,97 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "path" + "path/filepath" + "strings" + + "github.com/gernest/front" +) + +type Post struct { + Title string `json:"title"` + Link string `json:"link"` + Summary string `json:"summary"` + Date string `json:"date"` +} + +var posts []*Post + +func init() { + err := filepath.Walk("./blog/", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + fin, err := os.Open(path) + if err != nil { + return err + } + defer fin.Close() + + m := front.NewMatter() + m.Handle("---", front.YAMLHandler) + front, _, err := m.Parse(fin) + if err != nil { + return err + } + + p := &Post{ + Title: front["title"].(string), + Date: front["date"].(string), + Link: strings.Split(path, ".")[0], + } + + posts = append(posts, p) + + return nil + }) + + if err != nil { + panic(err) + } +} + +func main() { + http.HandleFunc("/api/blog/posts", writeBlogPosts) + http.HandleFunc("/api/blog/post", func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + name := q.Get("name") + + fin, err := os.Open(path.Join("./blog", name)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer fin.Close() + + m := front.NewMatter() + m.Handle("---", front.YAMLHandler) + _, body, err := m.Parse(fin) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + fmt.Fprintln(w, body) + }) + http.Handle("/dist/", http.FileServer(http.Dir("./frontend/static/"))) + http.HandleFunc("/", writeIndexHTML) + + log.Fatal(http.ListenAndServe(":9090", nil)) +} + +func writeBlogPosts(w http.ResponseWriter, r *http.Request) { + json.NewEncoder(w).Encode(posts) +} + +func writeIndexHTML(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "./frontend/static/dist/index.html") +} diff --git a/blog/cinemaquestria-orchestration-2015-03-13.markdown b/blog/cinemaquestria-orchestration-2015-03-13.markdown new file mode 100644 index 0000000..9a16bfb --- /dev/null +++ b/blog/cinemaquestria-orchestration-2015-03-13.markdown @@ -0,0 +1,122 @@ +--- +title: CinemaQuestria Orchestration +date: 2015-03-13 +--- + +CinemaQuestria Orchestration +============================ + +### Or: Continuous Defenstration in a Container-based Ecosystem + +I've been a core member of the staff for [CinemaQuestria](http://cinemaquestria.com) +for many months. In that time we have gone from shared hosting (updated by hand +with FTP) to a git-based deployment system that has won over the other +staffers. + +In this blogpost I'm going to take a look at what it was, what it is, and what +it will be as well as some challenges that have been faced or will be faced as +things advance into the future. + +The Past +-------- + +The site for CinemaQuestria is mostly static HTML. This was chosen mainly +because it made the most sense for the previous shared hosting environment as +it was the least surprising to set up and test. + +The live site content is about 50 MB of data including PDF transcripts of +previous podcast episodes and for a long time was a Good Enough solution that +we saw no need to replace it. + +However, being on shared hosting it meant that there was only one set of +authentication credentials and they had to be shared amongst ourselves. This +made sense as we were small but as we started to grow it didn't make much +sense. Combined with the fact that the copy of the site on the live server *was +pretty much the only copy of the site* we also lost disaster recovery points. + +Needless to say, I started researching into better solutions for this. + +The first solution I took a look at was AWS S3. It would let us host the CQ +site for about 0 dollars per month. On paper this looked amazing, until we +tried it and everyone was getting huge permissions issues. The only way to have +fixed this would have been to have everyone use the same username/password or +to have only one person do the deploys. In terms of reducing the [Bus +factor](https://en.wikipedia.org/wiki/Bus_factor) of the site's staff, this was +also unacceptable. + +I had done a lot of work with [Dokku-alt](https://github.com/dokku-alt/dokku-alt) +for hosting my personal things (this site is one of many hosted on this +server), so I decided to give it a try with us. + +The Present +----------- + +Presently the CQ website is hosted on a Dokku-alt server inside a container. +For a while while I was working on getting the warts out only I had access to +deploy code to the server, but quickly on I set up a private repo on my git +server for us to be able to track changes. + +Once the other staffers realized the enormous amount of flexibility being on +git gave us they loved it. From the comments I received the things they liked +the most were: + + - Accountability for who made what change + - The ability to rollback changes if need be + - Everyone being able to have an entire copy of the site and its history + +After the warts were worked out I gave the relevant people access to the dokku +server in the right way and the productivity has skyrocketed. Not only have +people loved how simple it is to push out new changes but they love how +consistent it is and the brutal simplicity of it. + +Mind you these are not all super-technically gifted people, but the command +line git client was good enough that not only were they able to commit and make +changes to the site, but they also took initiative and *corrected things they +messed up* and made sure things were consistent and correct. + +When I saw those commits in the news feed, I almost started crying tears of +happy. + +Nowadays our site is hosted inside a simple [nginx +container](https://registry.hub.docker.com/_/nginx/). In fact, I'll even paste +the entire Dockerfile for the site below: + +```Dockerfile +FROM nginx + +COPY . /usr/share/nginx/html +``` + +That's it. When someone pushes a new change to the server it figures out +everything from just those two lines of code. + +Of course, this isn't to say this system is completely free of warts. I'd love +to someday be able to notify the backrooms on skype every time a push to the +live server is made, but that might be for another day. + +The Future +---------- + +In terms of future expansion I am split mentally. On one hand the existing +static HTML is *hysterically fast* and efficient on the server, meaning that +anything such as a Go binary, Lua/Lapis environment or other web application +framework would have a very tough reputation to beat. + +I have looked into using Lapis for [this beta test site](http://cqsite-beta.apps.xeserv.us/), +but the fact that HTML is so dead easy to modify made that idea lose out. + +Maybe this is in the realm of something like [jekyll](http://jekyllrb.com/), +[Hugo](http://gohugo.io/) or [sw](https://github.com/jroimartin/sw) to take +care of. I'd need to do more research into this when I have the time. + +If you look at the website code currently a lot of it is heavily duplicated +code because the shared hosting version used to use Apache server-side +includes. I think a good place to apply these would be in the build in the +future. Maybe with a nice husking operation on build. + +--- + +Anyways, I hope this was interesting and a look into a side of CinemaQuestria +that most of you haven't seen before. The Season 5 premiere is coming up soon +and this poor server is going to get hammered like nothing else, so that will +be a nice functional test of Dokku-alt in a production setting. diff --git a/blog/coming-out-2015-12-01.markdown b/blog/coming-out-2015-12-01.markdown new file mode 100644 index 0000000..115ca6b --- /dev/null +++ b/blog/coming-out-2015-12-01.markdown @@ -0,0 +1,72 @@ +--- +title: Coming Out +date: 2015-12-01 +--- + +Coming Out +========== + +I'd like to bring up something that has been hanging over my head for a +long time. This is something I did try (and fail) to properly express way +back in middle school, but now I'd like to get it all of my chest and let +you know the truth of the matter. + +I don't feel comfortable with myself as I am right now. I haven't really +felt comfortable with myself for at least 10 years, maybe more; I'm not +entirely sure. + +At this point in my life I am really faced with a clear fork in the road. +I can either choose to continue living how I currently do, lying to myself +and others and saying everything is normal, or I can cooperate with the +reality that my brain is telling me that I don't feel comfortable with +myself as I have been for the last almost 22 years. I feel like I don't fit +inside my own skin. I think it is overall better for me to face the facts +and cooperate with reality. I have been repressing this off and on out of +fear of being shot down or not accepted the way I want to be seen to you +all. This has been a really hard thing for me to think through and even +harder for me to work up the courage to start taking action towards. This +is not a choice for me. I need to pursue this. + +In fact, I have been pursing this. My current business cards reflect who +I really am. My co-workers accept my abnormal status (when compared to the +majority of society), and even will help stand up for me if something goes +south with regards to it. + +I fully understand how much information this is to take in at once. I know +it will be difficult for you to hear that your firstborn son is actually a +daughter in a son's body, but I am still the same person. Most of the +changes that I want to pursue are purely cosmetic, but they are a bit more +noticeable than changing hair color. I feel that transitioning to living +as a woman like this will help me feel like I fit in with the world better +and help to make me more comfortable with who I am and how I want other +people to see me. Below I have collected some resources for you to look +through. They will help for you to understand my views better explained +in language you would be familiar with. + +I have been trialing a lot of possible first names to use, Zoe (the name +you were going to give me if I was born a girl) did come to mind, but after +meditating on it for a while I have decided that it doesn't fit me at all. +The name I am going with for now and eventually will change my official +documents to use is Christine Cadence Dodrill. + +Additionally I have been in a long-distance relationship with someone +since mid-June 2014. His name is Victor and he lives in Ottawa, Ontario. +He has been helping me a lot as I sort through all this; it has been a +godsend. He is a student in college for Computer Science. He knows and is +aware about my transition and has been a huge part of my emotional line +of support as I have been accepting these facts about who I am. + +--- + +Above is (a snipped version of) the letter I sent to my parents in the +last 48 hours. With this I have officially come out to all of my friends +and family as transgender. I am currently on hormone replacement therapy +and have been living full time as a woman. My workplace is very accepting +of this and has been a huge help over the last 7-8 months as I have +battled some of my inner demons and decided to make things official. + +I am now deprecating my old [facebook account](https://facebook.com/shadowh511) +and will be encouraging people to send friend requests and the like to my +[new account under the correct name](https://www.facebook.com/chrissycade1337). + +Thank you all for understanding and be well. diff --git a/blog/ffi-ing-golang-from-nim-for-fun-and-profit-2015-12-20.markdown b/blog/ffi-ing-golang-from-nim-for-fun-and-profit-2015-12-20.markdown new file mode 100644 index 0000000..7c26449 --- /dev/null +++ b/blog/ffi-ing-golang-from-nim-for-fun-and-profit-2015-12-20.markdown @@ -0,0 +1,281 @@ +--- +title: FFI-ing Golang from Nim for Fun and Profit +date: 2015-12-20 +--- + +FFI-ing Golang from Nim for Fun and Profit +========================================== + +As a side effect of Go 1.5, the compiler and runtime recently gained the +ability to compile code and run it as FFI code running in a C namespace. This +means that you can take any Go function that expresses its types and the like +as something compatible with C and use it from C, Haskell, Nim, Luajit, Python, +anywhere. There are some unique benefits and disadvantages to this however. + +A Simple Example +---------------- + +Consider the following Go file `add.go`: + +```go +package main + +import "C" + +//export add +func add(a, b int) int { + return a + b +} + +func main() {} +``` + +This just exposes a function `add` that takes some pair of C integers and then +returns their sum. + +We can build it with: + +``` +$ go build -buildmode=c-shared -o libsum.so add.go +``` + +And then test it like this: + +``` +$ python +>>> from ctypes import cdll +>>> a = cdll.LoadLibrary("./libsum.so") +>>> print a.add(4,5) +9 +``` + +And there we go, a Go function exposed and usable in Python. However now we +need to consider the overhead when switching contexts from your app to your Go +code. To minimize context switches, I am going to write the rest of the code in +this post in [Nim](http://nim-lang.org) because it natively compiles down to +C and has some of the best C FFI I have used. + +We can now define `libsum.nim` as: + +``` +proc add*(a, b: cint): cint {.importc, dynlib: "./libsum.so", noSideEffect.} + +when isMainModule: + echo add(4,5) +``` + +Which when ran: + +``` +$ nim c -r libsum +Hint: system [Processing] +Hint: libsum [Processing] +CC: libsum +CC: system +Hint: [Link] +Hint: operation successful (9859 lines compiled; 1.650 sec total; 14.148MB; Debug Build) [SuccessX] +9 +``` + +Good, we can consistently add `4` and `5` and get `9` back. + +Now we can benchmark this by using the `times.cpuTime()` proc: + +``` +# test.nim + +import + times, + libsum + +let beginning = cpuTime() + +echo "Starting Go FFI at " & $beginning + +for i in countup(1, 100_000): + let myi = i.cint + discard libsum.add(myi, myi) + +let endTime = cpuTime() + +echo "Ended at " & $endTime +echo "Total: " & $(endTime - beginning) +``` + +``` +$ nim c -r test +Hint: system [Processing] +Hint: test [Processing] +Hint: times [Processing] +Hint: strutils [Processing] +Hint: parseutils [Processing] +Hint: libsum [Processing] +CC: test +CC: system +CC: times +CC: strutils +CC: parseutils +CC: libsum +Hint: [Link] +Hint: operation successful (13455 lines compiled; 1.384 sec total; 21.220MB; Debug Build) [SuccessX] +Starting Go FFI at 0.000845 +Ended at 0.131602 +Total: 0.130757 +``` + +Yikes. This takes 0.13 seconds to do the actual computation of every number +i in the range of `0` through `100,000`. I ran this for a few hundred times and +found out that it was actually consistently scoring between `0.12` and `0.2` +seconds. Obviously this cannot be a universal hammer and the FFI is very +expensive. + +For comparison, consider the following C library code: + +``` +// libcsum.c +#include "libcsum.h" + +int add(int a, int b) { + return a+b; +} +``` + +``` +// libcsum.h +extern int add(int a, int b); +``` + +``` +# libcsum.nim +proc add*(a, b: cint): cint {.importc, dynlib: "./libcsum.so", noSideEffect.} + +when isMainModule: + echo add(4, 5) +``` + +and then have `test.nim` use the C library for comparison: + +``` +# test.nim + +import + times, + libcsum, + libsum + +let beginning = cpuTime() + +echo "Starting Go FFI at " & $beginning + +for i in countup(1, 100_000): + let myi = i.cint + discard libsum.add(myi, myi) + +let endTime = cpuTime() + +echo "Ended at " & $endTime +echo "Total: " & $(endTime - beginning) + +let cpre = cpuTime() +echo "starting C FFI at " & $cpre + +for i in countup(1, 100_000): + let myi = i.cint + discard libcsum.add(myi, myi) + +let cpost = cpuTime() + +echo "Ended at " & $cpost +echo "Total: " & $(cpost - cpre) +``` + +Then run it: + +``` +➜ nim c -r test +Hint: system [Processing] +Hint: test [Processing] +Hint: times [Processing] +Hint: strutils [Processing] +Hint: parseutils [Processing] +Hint: libcsum [Processing] +Hint: libsum [Processing] +CC: test +CC: system +CC: times +CC: strutils +CC: parseutils +CC: libcsum +CC: libsum +Hint: [Link] +Hint: operation successful (13455 lines compiled; 0.972 sec total; 21.220MB; Debug Build) [SuccessX] +Starting Go FFI at 0.00094 +Ended at 0.119729 +Total: 0.118789 + +starting C FFI at 0.119866 +Ended at 0.12206 +Total: 0.002194000000000002 +``` + +Interesting. The Go library must be doing more per instance than just adding +the two numbers and continuing about. Since we have two near identical test +programs for each version of the library, let's `strace` it and see if there is +anything that can be optimized. [The Go one](https://gist.github.com/Xe/e0cd06d1d93e3299102e) +and [the C one](https://gist.github.com/Xe/7641cdba5657a4e8435a) are both very simple +and it looks like the Go runtime is adding the overhead. + +Let's see what happens if we do that big loop in Go: + +``` +// add.go + +//export addmanytimes +func addmanytimes() { + for i := 0; i < 100000; i++ { + add(i, i) + } +} +``` + +Then amend `libsum.nim` for this function: + +``` +proc addmanytimes*() {.importc, dynlib: "./libsum.so".} +``` + +And finally test it: + +``` +# test.nim + +echo "Doing the entire loop in Go. Starting at " & $beforeGo + +libsum.addmanytimes() + +let afterGo = cpuTime() + +echo "Ended at " & $afterGo +echo "Total: " & $(afterGo - beforeGo) & " seconds" +``` + +Which yields: + +``` +Doing the entire loop in Go. Starting at 0.119757 +Ended at 0.119846 +Total: 8.899999999999186e-05 seconds +``` + +Porting the C library to have a similar function would likely yield similar +results, as would putting the entire loop inside Nim. Even though this trick +was only demonstrated with Nim and Python, it will work with nearly any +language that can convert to/from C types for FFI. Given the large number of +languages that do have such an interface though, it seems unlikely that there +will be any language in common use that you *cannot* write to bind to Go code. +Just be careful and offload as much of it as you can to Go. The FFI barrier +**really hurts**. + +--- + +This post's code is available [here](https://github.com/Xe/code/tree/master/experiments/go-nim). diff --git a/blog/getting-started-with-go-2015-01-28.markdown b/blog/getting-started-with-go-2015-01-28.markdown new file mode 100644 index 0000000..fa22f79 --- /dev/null +++ b/blog/getting-started-with-go-2015-01-28.markdown @@ -0,0 +1,148 @@ +--- +title: Getting Started with Go +date: 2015-01-28 +--- + +Getting Started with Go +======================= + +Go is an exciting language made by Google for systems programming. This article +will help you get up and running with the Go compiler tools. + +System Setup +------------ + +First you need to install the compilers. + +```console +$ sudo apt-get install golang golang-go.tools +``` + +`golang-go.tools` contains some useful tools that aren't part of the standard +Go distribution. + +Shell Setup +----------- + +Create a folder in your home directory for your Go code to live in. I use +`~/go`. + +```console +$ mkdir -p ~/go/{bin,pkg,src} +``` + +`bin` contains go binaries that are created from `go get` or `go install`. +`pkg` contains static (`.a`) compiled versions of go packages that are not go +programs. `src` contains go source code. + +After you create this, add +[this](https://github.com/Xe/dotfiles/blob/master/.zsh/go-completion.zsh) and +the following to your zsh config: + +```sh +export GOPATH=$HOME/go +export PATH=$PATH:/usr/lib/go/bin:$GOPATH/bin +``` + +This will add the go compilers to your `$PATH` as well as programs you install. + +Rehash your shell config (I use +a [`resource`](https://github.com/Xe/dotfiles/blob/master/.zsh/resource.zsh#L3) +command for this) and then run: + +```console +$ go env +GOARCH="amd64" +GOBIN="" +GOCHAR="6" +GOEXE="" +GOHOSTARCH="amd64" +GOHOSTOS="linux" +GOOS="linux" +GOPATH="/home/xena/go" +GORACE="" +GOROOT="/usr/lib/go" +GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64" +TERM="dumb" +CC="gcc" +GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread" +CXX="g++" +CGO_ENABLED="1" +``` + +This will verify that the go toolchain knows where the go compilers are as well +as where your `$GOPATH` is. + +Testing +------- + +To test the go compilers with a simple +[todo command](http://github.com/mattn/todo), run this: + +```console +$ go get github.com/mattn/todo +$ todo add foo +$ todo list +☐ 001: foo +``` + +Vim Setup +--------- + +For Vim integration, I suggest using the +[vim-go](https://github.com/fatih/vim-go) plugin. This plugin used to be part +of the standard Go distribution. + +To install: + +1. Add `Plugin 'fatih/vim-go'` to the plugins part of your vimrc. +2. Run these commands: + +```console +$ vim +PluginInstall +qall +$ vim +GoInstallBinaries +qall +``` + +This will install the go oracle and the go autocompletion daemon gocode as well +as some other useful tools that will integrate seamlessly into vim. This will +also run `gofmt` on save to style your code to the standard way to write Go +code. + +Resources +--------- + +[Effective Go](https://golang.org/doc/effective_go.html) and the +[language spec](https://golang.org/ref/spec) provide a nice overview of the +syntax. + +The Go [blog](http://blog.golang.org) contains a lot of detailed articles +covering advanced and simple Go topics. +[This page](https://golang.org/doc/#articles) has a list of past articles that +you may find useful. + +The Go standard library is a fantastic collection of Go code for solving many +problems. In some cases you can even write entire programs using only the +standard library. This includes things like web application support, tarfile +support, sql drivers, support for most kinds of commonly used crypto, command +line flag parsing, html templating, and regular expressions. A full list of +the standard library packages can be found [here](http://godoc.org/-/go). + +Variable type declarations will look backwards. It takes a bit to get used to +but makes a lot of sense once you realize it reads better left to right. + +For a nice primer on building web apps with Go, codegangsta is writing a book +on the common first steps, starting from the standard library and working up. +You can find his work in progress book +[here](http://codegangsta.gitbooks.io/building-web-apps-with-go/). + +Go has support for unit testing baked into the core language tools. You can +find information about writing unit tests [here](http://golang.org/pkg/testing/). + +When creating a new go project, please resist the urge to make the folder in your +normal code folder. Drink the `$GOPATH` koolaid. Yes it's annoying, yes it's the +language forcing you to use its standard. Just try it. It's an amazingly useful +thing once you get used to it. + +Learn to love godoc. Godoc lets you document code like +[this](https://gist.github.com/Xe/b973e30d81280899955d). This also includes an +example of the builtin unit testing support. diff --git a/blog/metaprogramming-partial-application-2015-08-26.markdown b/blog/metaprogramming-partial-application-2015-08-26.markdown new file mode 100644 index 0000000..61fc9a9 --- /dev/null +++ b/blog/metaprogramming-partial-application-2015-08-26.markdown @@ -0,0 +1,430 @@ +--- +title: "Metaprogramming: Partial Application..." +date: 2015-08-26 +--- + +Metaprogramming: Partial Application and Currying 101 +===================================================== + +The title of this post looks intimidating. There's a lot of words there that +look like they are very complicated and will take a long time to master. In +reality, they are really very simple things. Let's start with a mundane example +and work our way up to a real-world bit of code. Let's begin with a small +story: + +--- + +ACMECorp has a world-renowned Python application named Itera that is known for +its superb handling of basic mathematic functions. It's so well known and +industry proven that it is used in every school and on every home computer. You +have just accepted a job there as an intermediate programmer set to do +maintenance on it. Naturally, you are very excited to peek under the hood of +this mysterious and powerful program and offer your input to make it even +better for the next release and its users. + +Upon getting there, you settle in and look at your ticket queue for the day. +A user is complaining that whenever they add `3` and `5`, they get `7` instead +of `8`, which is what they expected. Your first step is to go look into the +`add3` function and see what it does: + +```Python +def add1(x): + return x + 1 + +def add2(x): + return x + 2 + +def add3(x): + return x + 2 + +def add4(x): + return x + 4 +``` + +You are aghast. Your company's multi-billion dollar calculator is brought to +its knees by a simple copy-paste error. You wonder, "how in Sam Hill are these +people making any money???" (The answer, of course, is that they are a big +enterprise corporation) + +You let your boss know about the bad news, you are immediately given any +resource in the company that you need to get this mission-critical problem +solved for *any input*. Yesterday. Without breaking the API that the rest of +the program has hard-coded in. + +--- + +Let's look at what is common about all these functions. The `add*` family of +functions seems to all be doing one thing consistently: adding one number to +another. + +Let's define a function called `add` that adds any two numbers: + +```Python +def add(x, y): + return x + y +``` + +This is nice, but it won't work for the task we were given, which is to not +break the API. + +Let's go over what a function is in Python. We can define a function as +something that takes some set of Python values and produces some set of Python +values: + +```haskell +PythonFunction :: [PythonValue] -> [PythonValue] +``` + +We can read this as "a Python function takes a set of Python values and +produces a set of Python values". Now we need to define what a Python value +actually is. To keep things simple, we're only going to define the following +types of values: + +- `None` -> no value +- `Int` -> any whole number (Python calls this `int`) +- `Text` -> any string value (Python calls this `str`) +- `Function` -> something that takes and produces values + +Python [itself has a lot more types that any value can be](https://docs.Python.org/3.4/library/stdtypes.html), +but for the scope of this blog post, this will do just fine. + +Now, since a function can return a value and a function is a value, let's see +what happens if you return a function: + +```python +def outer(): + def inner(): + return "Hello!" + return inner +``` + +And in the repl: + +``` +>>> type(outer) +<type 'function'> +``` + +So `outer` is a function as we expect. It takes `None` (in Python, a function +without arguments has `None` for the type of them) and returns a function that +takes `None` and that function returns `Text` containing `"Hello!"`. +Let's make sure of this: + +``` +>>> outer()() +'Hello!' +>>> type(outer()()) +<type 'str'> +``` + +Yay! When nothing is applied to the result of applying nothing to `outer`, it +returns the `Text` value `"Hello!"`. We can define the type of `outer` as the +following: + +```haskell +outer :: None -> None -> Text +``` + +Now, let's use this for addition: + +```python +# add :: Int -> Int -> Int +def add(x): + def inner(y): + return x + y + + return inner +``` + +And in the repl: + +``` +>>> add(4)(5) +9 +``` + +A cool feature about this is that now we can dip into something called Partial +Application. Partial application lets you apply part of the arguments of +a function and you get another function out of it. Let's trace the type of the +`inner` function inside the `add` function, as well as the final computation +for clarity: + +```python +# add :: Int -> Int -> Int +def add(x): + # inner :: Int -> Int + def inner(y): + return x + y # :: Int + + return inner +``` + +Starting from the inside, we can see how the core computation here is `x + y`, +which returns an `Int`. Then we can see that `y` is passed in and in the scope +also as an `Int`. Then we can also see that `x` is passed in the outermost +layer as an int, giving it the type `Int -> Int -> Int`. Since `inner` is +a value, and a Python variable can contain any Python value, let's make +a function called `increment` using the `add` function: + +```python +# increment :: Int -> Int +increment = add(1) +``` + +And in the repl: + +``` +>>> increment(50) +1 +``` + +`increment` takes the integer given and increases it by 1, it is the same thing +as de |
