gdritter repos infinite-negative-utility / 6f32339
basic lektor project for inf frontpage Getty Ritter 5 years ago
21 changed file(s) with 486 addition(s) and 0 deletion(s). Collapse all Expand all
1 @font-face {
2 font-family: league-spartan;
3 src: url("/static/leaguespartan-bold.ttf");
4 }
5
6 body {
7 font-family: "Arial", "Helvetica", sans-serif;
8 font-size: 14pt;
9 margin-left: 0px;
10 margin-right: 0px;
11 }
12 .header {
13 background-color: #444;
14 color: #fff;
15 width: 100%;
16 margin: 0 auto;
17 }
18 h1 {
19 padding-left: 40px;
20 text-transform: uppercase;
21 font-family: league-spartan;
22 letter-spacing: 5px;
23 height: 80px;
24 position: relative;
25 }
26 .text {
27 padding-left: 50px;
28 position: absolute;
29 top: 50%;
30 margin-top: -15px;
31 }
32 .menu {
33 display: inline-block;
34 text-transform: uppercase;
35 font-size: 16pt;
36 letter-spacing: 2px;
37 width: 25%
38 margin-left: 20px;
39 margin-right: auto;
40 line-height: 140%;
41 }
42 .menu ul {
43 list-style-type: none;
44 }
45 .content {
46 padding: 40px;
47 float: right;
48 display: inline-block;
49 width: 70%;
50 }
51 a:link { color: #bbbbbb; }
52 a:hover { color: #bbbbbb; }
53 a:active { color: #999999; }
54 a:visited { color: #999999; }
1 title: About
2 ---
3 body:
4
5 ![a photo of getty](/about/getty.jpg)
6
7 what's up
Binary diff not shown
1 title: Infinite Negative Utility
2 ---
3 body:
4
5 Hi, I'm Getty Ritter. I'm a kind of programmer-artist-writer-dilettante, and this is my mostly-tech-focused web site.
6
7 Right now I live in Portland, Oregon, and work as a computer scientist and engineer. My background is primarily in computer science, with a focus on programming language theory and proof theory, and in linguistics, with a focus on historical linguistics.
8
9 I also write prose—mostly [short stories](http://librarianofalexandria.com/category/stories/) and [drabbles](http://librarianofalexandria.com/category/fascicles/)—and create art in the form of [paintings](http://thefireattheshoemakersestate.tumblr.com/tagged/painting), [sketches](http://thefireattheshoemakersestate.tumblr.com/tagged/sketching), [ink drawings](http://thefireattheshoemakersestate.tumblr.com/tagged/ink-drawing), and [pixel art](http://thefireattheshoemakersestate.tumblr.com/tagged/pixel-art). I'm an experimental cook, amateur mixologist, and not a particularly good homebrewer. I speak half a dozen languages poorly and none well. Past hobbies that I'd like to get back to some day include blacksmithing, creating and maintaing bonsai, and conlang creation—but who has time?
1 _model: page
2 ---
3 title: config-ini
4 ---
5 body:
6
7 The `config-ini` library is a Haskell library for doing elementary INI file parsing in a quick and painless way. You can find the source code [on Github](https://github.com/aisamanra/config-ini) and the full documentation for the library [on Hackage](http://hackage.haskell.org/package/config-ini).
8
9 There are two ways of using the library: one of them involves a traditional monadic DSL which parses field-by-field, and the other is a powerful bidirectional DSL which allows you to use the same declarative specification to parse, serialize, and diff-minimally update INI files.
10
11 Consider the following basic INI file:
12
13 ~~~.ini
14 [NETWORK]
15 host = example.com
16 port = 7878
17
18 # here is a comment
19 [LOCAL]
20 user = terry
21 ~~~
22
23 We want to parse this into a Haskell data structure defined using this type, with its associated lenses:
24
25 ~~~haskell
26
27 data Config = Config
28 { _cfHost :: String
29 , _cfPort :: Int
30 , _cfUser :: Maybe Text
31 } deriving (Eq, Show)
32
33 makeLenses ''Config
34 ~~~
35
36 Using `config-ini`'s basic API, we can extract the fields using basic functions like `fieldOf` and `section`, which lookup and deserialize values from the INI file, and construct a `Config` value from those:
37
38 ~~~haskell
39 configParser :: IniParser Config
40 configParser = do
41 (host, port) <- section "NETWORK" $ do
42 host <- fieldOf "host" string
43 port <- fieldOf "port" number
44 pure (host, port)
45 user <- sectionMb "LOCAL" $ field "user"
46 return Config
47 { _cfHost = host
48 , _cfPort = port
49 , _cfUser = user
50 }
51 ~~~
52
53 In order to use `config-ini`'s bidirectional API, we instead have to use the generated lenses and associate them with _descriptions_ of how to look up those individual fields, like this:
54
55 ~~~haskell
56 configSpec :: IniSpec Config ()
57 configSpec = do
58 section "NETWORK" $ do
59 cfHost .= field "host" string
60 cfPort .= field "port" number
61 section "LOCAL" $ do
62 cfUser .=? field "user"
63 ~~~
64
65 This defines a specification that we can use to create a parser, but in order to actually construct a value, we need a default `Config` value to supply to it, which we pass along with the `IniSpec` to the `ini` function, which gives us a value of type `Ini`, from which we can derive a parser:
66
67 ~~~haskell
68 configIni :: Ini Config
69 configIni =
70 let defConfig = Config "localhost" 8080 Nothing
71 in ini defConfig configSpec
72
73 myParseIni :: Text -> Either String Config
74 myParseIni t = fmap getIniValue (parseIni t configIni)
75 ~~~
76
77 However, we can also _serialize_ the `Ini` value, which takes the default value and turns it into a textual INI file. We can also use the `Ini` value to parse a file into a new `Ini` value, update it, and then re-serialize: the `Ini` value will contain all of the "structural" information about the file, like whitespace and comments and ordering, which means that the update is _diff-minimal_: all unchanged values will remain in the same order, changed value will be modified in-place with retained comments, and new values will appear grouped together at the end of their sections.
1 _model: page
2 ---
3 title: Libraries
1 _model: page
2 ---
3 title: cube-cotillion
4 ---
5 body:
6
7 The `cube-cotillion` library is a heavily Scotty-inspired framework
8 for writing services over SSH.
9
10 ## Example
11
12 This example allows anyone to authenticate, and responds to two
13 commands, `greet` and `greet [name]`, with a short greeting.
14
15 ~~~.haskell
16 {-# LANGUAGE OverloadedStrings #-}
17
18 module Main where
19
20 import Data.Monoid (mconcat)
21 import Network.CubeCotillion
22
23 main :: IO ()
24 main = do
25 key <- loadKey "server-keys"
26 cubeCotillion 8080 key $ do
27 cmd "greet" $ do
28 bs "Hello, world!\n"
29 cmd "greet :name" $ do
30 name <- param "name"
31 bs $ mconcat ["Hello, ", name, "!\n"]
32 ~~~
33
34 While running this service on localhost, we can connect to and
35 use it like so:
36
37 ~~~
38 [gdritter@mu ~]$ ssh -p 8080 localhost greet
39 Hello, world!
40 [gdritter@mu ~]$ ssh -p 8080 localhost greet Eberhardt
41 Hello, Eberhardt!
42 ~~~
43
44 ## Why?
45
46 HTTP is often used as a protocol for exposing certain kinds of
47 services, but HTTP also lacks certain kinds of built-in features,
48 which are often reimplemented in various different ways: for
49 example, connection multiplexing, compression of conveyed
50 information, and user authentication and identity. All of these
51 are features trivially supported by the SSH protocol already.
52 Additionally, tools for working with SSH are ubiquitous, and
53 developers often already have existing SSH identities.
54
55 That doesn't necessarily mean that SSH is a great protocol to
56 use to build services on top of. I frankly don't _know_ if
57 that would be a good idea or not! That's why `cube-cotillion`
58 exists: to experiment with building these kinds of services
59 in a quick and easy way.
60
61 ## Why The Name?
62
63 The design of the library is heavily inspired by the
64 lightweight Haskell web frameworks
65 [Scotty](http://hackage.haskell.org/package/scotty) and
66 [Spock](http://hackage.haskell.org/package/Spock), both
67 of which are named after Star Trek Characters. I figured
68 I should follow suit, and choose the name of one of my
69 [favorite Star Trip characters, too](https://www.youtube.com/watch?v=O2XOLoeBPEk).
70
1 _model: page
2 ---
3 title: s-cargot
4 ---
5 body:
6
7 S-Cargot is a library for parsing and emitting S-expressions, designed to be flexible, customizable, and extensible. Different uses of S-expressions often understand subtly different variations on what an S-expression is. The goal of S-Cargot is to create several reusable components that can be repurposed to nearly any S-expression variant.
8
9 The source code for S-Cargot is [on Github](https://github.com/aisamanra/s-cargot) and the full library documentation is [on Hackage](http://hackage.haskell.org/package/s-cargot).
10
11 The library is _very_ flexible, and is designed to accommodate defining _families_ of s-expression languages rather than assuming that all s-expression formats share much beyond their parenthesis-based syntax. Here, is a large, verbose example which a minimal arithmetic language with both decimal and hexadecimal numeric literals and then uses `s-cargot` to derive both serializers and pretty-printers for the language:
12
13 ~~~~haskell
14 {-# LANGUAGE OverloadedStrings #-}
15
16 module SCargotExample where
17
18 import Control.Applicative ((<|>))
19 import Data.Char (isDigit)
20 import Data.SCargot
21 import Data.SCargot.Repr.Basic
22 import Data.Text (Text, pack)
23 import Numeric (readHex)
24 import Text.Parsec (anyChar, char, digit, many1, manyTill, newline, satisfy, string)
25 import Text.Parsec.Text (Parser)
26
27 -- Our operators are going to represent addition, subtraction, or
28 -- multiplication
29 data Op = Add | Sub | Mul deriving (Eq, Show)
30
31 -- The atoms of our language are either one of the aforementioned
32 -- operators, or positive integers
33 data Atom = AOp Op | ANum Int deriving (Eq, Show)
34
35 -- Once parsed, our language will consist of the applications of
36 -- binary operators with literal integers at the leaves
37 data Expr = EOp Op Expr Expr | ENum Int deriving (Eq, Show)
38
39 -- Conversions to and from our Expr type
40 toExpr :: SExpr Atom -> Either String Expr
41 toExpr (A (AOp op) ::: l ::: r ::: Nil) = EOp op <$> toExpr l <*> toExpr r
42 toExpr (A (ANum n)) = pure (ENum n)
43 toExpr sexpr = Left ("Unable to parse expression: " ++ show sexpr)
44
45 fromExpr :: Expr -> SExpr Atom
46 fromExpr (EOp op l r) = A (AOp op) ::: fromExpr l ::: fromExpr r ::: Nil
47 fromExpr (ENum n) = A (ANum n) ::: Nil
48
49 -- Parser and serializer for our Atom type
50 pAtom :: Parser Atom
51 pAtom = ((ANum . read) <$> many1 digit)
52 <|> (char '+' *> pure (AOp Add))
53 <|> (char '-' *> pure (AOp Sub))
54 <|> (char '*' *> pure (AOp Mul))
55
56 sAtom :: Atom -> Text
57 sAtom (AOp Add) = "+"
58 sAtom (AOp Sub) = "-"
59 sAtom (AOp Mul) = "*"
60 sAtom (ANum n) = pack (show n)
61
62 -- Our comment syntax is going to be Haskell-like:
63 hsComment :: Parser ()
64 hsComment = string "--" >> manyTill anyChar newline >> return ()
65
66 -- Our custom reader macro: grab the parse stream and read a
67 -- hexadecimal number from it:
68 hexReader :: Reader Atom
69 hexReader _ = (A . ANum . rd) <$> many1 (satisfy isHexDigit)
70 where isHexDigit c = isDigit c || c `elem` hexChars
71 rd = fst . head . readHex
72 hexChars :: String
73 hexChars = "AaBbCcDdEeFf"
74
75 -- Our final s-expression parser and printer:
76 myLangParser :: SExprParser Atom Expr
77 myLangParser
78 = setComment hsComment -- set comment syntax to be Haskell-style
79 $ addReader '#' hexReader -- add hex reader
80 $ setCarrier toExpr -- convert final repr to Expr
81 $ mkParser pAtom -- create spec with Atom type
82
83 mkLangPrinter :: SExprPrinter Atom Expr
84 mkLangPrinter
85 = setFromCarrier fromExpr
86 $ setIndentStrategy (const Align)
87 $ basicPrint sAtom
88
89 >>> decode myLangParser "(+ (* 2 20) 10) (* 10 10)"
90 [EOp Add (EOp Mul (ENum 2) (ENum 20)) (ENum 10),EOp Mul (ENum 10) (ENum 10)]
91 ~~~~
92
1 _model: page
2 ---
3 title: telml
4 ---
5 body:
6
7 **TeLML** (short for _TeX-Like Markup Language_) is a markup language I created for personal projects, specifically to serve as a lightweight but extensible markup language. I've implemented it in several languages, but the current reference implementation is in Haskell, and can be found [on Gitub](https://github.com/aisamanra/telml). In addition to using it locally as a markup language for projects-in-progress, it is also the markup language that powers [What Happens When Computer](https://what.happens.when.computer/), my short-posts-on-technical-topics blog.
8
9 The core of TeLML is the _tag_, which looks mostly like a LaTeX command invocation (e.g. `\em{foo}`), with the following major differences:
10 - All tags in TeLML have a _mandatory_ payload, which is surrounded in curly braces. Thus, `\br` is not a valid TeLML tag, but `\br{}` is.
11 - All tags in TeLML can have multiple "arguments" separated by vertical bars, so a tag with multiple values might look like `\link{https://duckduckgo.com/|this}`.
12 - All special characters can be escaped with another backslash, so that `\\br\{\}` is the TeLML transcription of the string `\br{}` rather than a nullary tag.
13
14 The TeLML format is split into two parts: the _core_ format, which only defines the data model and how to parse and serialize it, and the _markup_ format, which adds a set of HTML-like basic tags on top (including inline tags like `\em{...}` and `\strong{...}` as well as block-level tags like `\blockquote{...}` and `\code{...}`.) The libraries linked to above also allow uses that are _extensible_, in which new bespoke tags can be added for particular purposes.
15
16 For a more thorough description, see the following links:
17 - [A description of the exact grammar and data model used in TeLML](https://git.gdritter.com/telml/blob/master/telml/README.md)
18 - [The TeLML markup module](https://git.gdritter.com/telml/blob/master/telml-markup/README.md) that defines the tag semantics as well as an API for adding new custom tags
19 - [A document giving short rationale for why I wrote this instead of using another markup language](https://git.gdritter.com/telml/blob/master/README.md)
1 _model: page
2 ---
3 title: Bricoleur
4 ---
5 body:
6
7 The `bricoleur` tool is an unfinished personal tool designed for drafting blog posts that include source code.
8
9 The fundamental problem that `bricoleur` aims to solve is that it's easy for executable source code to get out of sync with a blog-post-in-progress. There's a temptation (for me, at least) to edit variable names or modify source layout of source code snippets as I write a blog post, and if I forget to update a variable name somewhere or make a typo, it's possible I've produced a blog post that includes invalid source code. I sometimes avoid this by always copy/pasting from working source examples, but that can be tedious.
10
11 With `bricoleur`, I start writing my post with placeholders, which are indicated with guillemets. The post can be in any format, but for my purposes here, let's assume it's in Markdown. I can write a post like this, say, in a file called `post.md`:
12
13 ``````
14 The hello world program in Python looks like this:
15 ```python
16 «hello»
17 ```
18 ``````
19
20 I can then write, say in `main.py`, the actual source code I wanted to include:
21
22 ```python
23 print("Hello, world!")
24 ```
25
26 I can then tie them together with a "bricoleur" file, which is conventionally just named `bricoleur`:
27
28 ```
29 (document
30 # this tells us that we're assembling "post.md"
31 "post.md"
32 # and then we define the fragments we care about
33 {
34 # this fragment's name is "hello"
35 name "hello"
36 # to test this fragment, we run "python main.py"
37 cmd [ "python main.py" ]
38 # and this fragment should be replaced by the contents
39 # the file "main.py"
40 expose (file "main.py")
41 }
42 )
43 ```
44
45 Once I've done this, I can use the `bricoleur` tool in two ways: for one, I can run `bricoleur test` and it'll execute the relevant commands and tell me whether they succeeded, and for another, I can run `bricoleur splice` and it'll stitch the contents of the source file at the relevant place in the original, producing the output:
46
47 ``````
48 The hello world program in Python looks like this:
49 ```python
50 print("Hello, world!")
51 ```
52 ``````
53
54 I can do more than this: for example, instead of exposing the entirety of a file, I can indicate particular chunks of the file via special comments, and I can expose multiple files, or even have multiple "subprojects" that each get tested individually in different ways. The [README file](https://git.gdritter.com/bricoleur/blob/master/README.md) has some more examples.
55
56 I haven't properly released this tool yet, and thus make no guarantees about it, but if you're interested in trying it out, you can find the source code [on my personal git server](https://git.gdritter.com/bricoleur/), or clone the repo with
57
58 ```
59 $ git clone https://git.gdritter.com/bricoleur/
60 ```
61
62 This will also require a copy of the [`adnot`](https://git.gdritter.com/adnot/) library.
1 title: Projects
2 ---
3 body:
4
5 This is a list of the projects:
6
7 * Project 1
8 * Project 2
9 * Project 3
1 _model: page
2 ---
3 title: Matterhorn
4 ---
5 body: I haven't been contributing as actively recently, but I was one of the core developers and maintainers of the [Matterhorn](https://github.com/matterhorn-chat/matterhorn) chat client, which is a rich terminal-based chat client intended for the [Mattermost](https://mattermost.com/) chat system.
Binary diff not shown
1 [project]
2 name = infinite-negative-utility
1 [model]
2 name = Page
3 label = {{ this.title }}
4
5 [fields.title]
6 label = Title
7 type = string
8
9 [fields.body]
10 label = Body
11 type = markdown
1 <!doctype html>
2 <meta charset="utf-8">
3 <link rel="stylesheet" href="{{ '/static/style.css'|url }}">
4 <title>Infinite Negative Utility: {% block title %}index{% endblock %}</title>
5
6 <style type="text/css" >
7 </style>
8
9 <body>
10 <div class="header"><h1><img src="/static/inu-logo-small.png"/><span class="text" >infinite negative utility</span></h1></div>
11 <div >
12 <nav class="menu">
13 <ul class="nav navbar-nav">
14 <li><a href="/">home</a></li>
15 <li>projects</li>
16 <ul>
17 <li><a href="/projects/bricoleur">bricoleur</a></li>
18 <li><a href="/projects/matterhorn">matterhorn</a></li>
19 </ul>
20 <li>libraries</li>
21 <ul>
22 <li><a href="/libraries/s-cargot">s-cargot</a></li>
23 <li><a href="/libraries/config-ini">config-ini</a></li>
24 <li><a href="/libraries/cube-cotillion">cube cotillion</a></li>
25 <li><a href="/libraries/telml">telml</a></li>
26 </ul>
27 <li>art & writing</li>
28 <ul>
29 <li><a href="https://thefireattheshoemakersestate.tumblr.com/">art</a></li>
30 <li><a href="http://librarianofalexandria.com/">prose</a></li>
31 <li><a href="http://blog.infinitenegativeutility.com/">technical</a></li>
32 <li><a href="http://what.happens.when.computer/">informative</a></li>
33 <li><a href="https://twitter.com/aisamanra">tweets</a></li>
34 <li><a href="https://mastodon.social/users/keweddji">toots</a></li>
35 </ul>
36 <li><a href="http://gdritter.com/resume.pdf">résumé</a></li>
37 <li><a href="/about">about</a></li>
38 </ul>
39 </nav>
40 <div class="content">
41 {% block body %}{% endblock %}
42 </div>
43 </div>
44 </body>
1 {% macro render_pagination(pagination) %}
2 <div class="pagination">
3 {% if pagination.has_prev %}
4 <a href="{{ pagination.prev|url }}">&laquo; Previous</a>
5 {% else %}
6 <span class="disabled">&laquo; Previous</span>
7 {% endif %}
8 | {{ pagination.page }} |
9 {% if pagination.has_next %}
10 <a href="{{ pagination.next|url }}">Next &raquo;</a>
11 {% else %}
12 <span class="disabled">Next &raquo;</span>
13 {% endif %}
14 </div>
15 {% endmacro %}
1 {% extends "layout.html" %}
2 {% block title %}{{ this.title }}{% endblock %}
3 {% block body %}
4 <h2>{{ this.title }}</h2>
5 {{ this.body }}
6 {% endblock %}