Luminescent Dreams

Androgyne with Surgery

January 01, 0001

I did not sleep well the night before. Thoughts of missing my alarm plague me the night before an important but extremely early event. Most of the time I have to be moving for a flight, but this day I needed to be at the surgical center at 5am.

_DSC2317.web

Daria had it under control. Zie woke me at 4. We were out the door at 4:30, and we arrived at the surgical center at 4:50. After that, I put my fate into everyone else’s hands as they guided me into the building and prepared my IV. Two different doctors, first my surgeon then the anesthesiologist. Strangely, the anesthesiologist did not know about spironolactone and left with the words “I have some research to do.” A few minutes later, the nurse took me to the operating room, helped me get positioned on the table, said she would go find another electric blanket for me, and then I woke up in my bed at home.

IMG_5124

I think no experience will ever match that for strangeness. I have seen pictures of “waking up”, and of getting myself into my car, but I remember absolutely nothing else.

I saw myself in the mirror some hours later. As soon as I felt up for doing so, I undressed to look in the mirror, and I nearly started crying. I felt relief beyond what I imagined, with the feeling that my body, finally, was my own.

20160911_104525

I cannot remember a time that I did not want breasts. Through all of medical transition, though, I have had to constantly ask myself whether I am an androgyne or a trans woman. Everything about my transition looks like what a trans woman would do, except that I did it slowly, and in a different order. At it’s core, how much can I alter my body before I cross into being a woman?

IMG_5319

When Washburn passed a few weeks ago, I understood in my grief that the term “mother” applies to me. “Mother” is a job. Not a gender. And it has nothing to do with biology. Even I find myself harboring ideas of biological essentialism, and even I must take time to expunge those ideas.

My transition has always been about giving myself the body I want, the one I can feel comfortable in. The things I have done to my body do not change my gender, but they have made me much more comfortable in my body.

I still do not have a gender. I am an androgyne, and I am proud of that.

_DSC2662

Beehive Minecraft Server

January 01, 0001

Beehive Minecraft Server

Minecraft 1.20.1

Mods

Server Mods: Client Mods
audioplayer
create create
fabric fabric
lithium
simple voice chat simple voice chat
towns and towers

Optional, but cool, client mods:

  • optimizations:
    • sodium
    • indium
    • lithium
  • ambiance:
    • ambient sounds
    • areas
    • better animations
    • bobby
  • tech utilities:
    • litematica
    • tweakeroo
    • minihud
    • replaymod

Datapacks

  • anti-enderman grief
  • double shulker shells
  • silence mobs
  • multiplayer sleep
  • armor statues
  • redstone rotation wrench
  • terracotta rotation wrench
  • player head drops

Color Multipliers

January 01, 0001

I hacked out a small GTK utility last night. The problem, and this really only applies to Linux, is that the Sony NEX-series cameras don’t record the color temperature and green/magenta balance in the exif data. They do, however, record an RGGB line like so:

WB RGGB Levels                  : 2068 1024 1024 2160

ufraw on Linux doesn’t recognize this. With a couple of experiments I figured out what these values mean and how to translate them into something useful for ufraw. And then I reinvented it too many times and finally decided to encode it into an application.

color-temp will, given the string above, calculate out the RGB multipliers that UFraw uses for developing a raw file. I have placeholders there for color temperature and green/magenta balance (which is isomorphic to the RGB multipliers, but friendlier in some ways), but that is tricky to calculate and I haven’t succeeded at that yet.

I’m working on getting the application packaged, and when I do I’ll make a debian package available for download. Until then, just snag and build the code:

color-temp$ cabal install --dependencies-only
color-temp$ cabal install
anywhere$ color-temp

And then just paste the RGGB levels (2068 1024 1024 2160) into the RGGB field.

Computer Graphics in Haskell

January 01, 0001

Welcome

Functions

circleArea :: Float -> Float
circleArea r = pi * r ^ 2

rectangleArea :: Float -> Float -> Float
rectangleArea l w = l * w

triangleArea :: Float -> Float -> Float
triangleArea h b = h * b / 2

totalCirclesArea :: [Float] -> Float
totalCirclesArea rs = sum (map circleArea rs)

Function Structure

functionName :: TypeParam1 -> TypeParam2 -> ... -> TypeResult
functionName param1 param2 ... =

Functions

pi :: Float

Functions

not :: Bool -> Bool

not is a function which takes a single parameter (a Boolean) and returns a result (another Boolean).

Functions

(++) :: String -> String -> String

++ is a function which takes one parameter (a String) and returns a new function that takes a single parameter (also a String) and returns a result.

Functions

(++) :: String -> String -> String

> :t (++) "a"
(++) "a" :: String -> String

Functions

(++) :: String -> String -> String

> :t (++) "a" "b"
(++) "a" "b" :: String
> (++) "a" "b"
"ab"

Functions

(++) :: String -> String -> String

> :t (++) "a" "b"
(++) "a" "b" :: String
> (++) "a" "b"
"ab"
> "a" ++ "b"
"ab"

Polymorphism

(++) :: [a] -> [a] -> [a]

++ is a function which takes one parameter (a list of anything) and returns a new function that takes a single parameter (also a list, but of the same thing) and returns a result. ++ works on the list structure and cares nothing for the data contained in the list.

Partial Application and more Polymorphism

map :: (a -> b) -> [a] -> [b]

Map is a function which takes one parameter (itself a function) and returns a new function that takes a single parameter and returns a result.

Partial Application and more Polymorphism

map :: (a -> b) -> [a] -> [b]

> :t map not
map not :: [Bool] -> [Bool]

Partial Application and more Polymorphism

map :: (a -> b) -> [a] -> [b]

> :t map not
map not :: [Bool] -> [Bool]

> :t map not [True, True, False]
map not [True, True, False] :: [Bool]
> map not [True, True, False]
[False,False,True]

Type Constraints

show :: Show a => a -> String

> show 15
"15"

show is a function that takes a value, which must implement the Show interface, and returns a String.

Type Constraints

show :: Show a => a -> String

class Show a where
  show :: a -> String

show is a function that takes a value, which must implement the Show interface, and returns a String.

Building polymorphism

circleArea :: Float -> Float
circleArea r = pi * r ^ 2

rectangleArea :: Float -> Float -> Float
rectangleArea l w = l * w

triangleArea :: Float -> Float -> Float
triangleArea h b = h * b / 2

totalCirclesArea :: [Float] -> Float
totalCirclesArea rs = sum (map circleArea rs)

Building polymorphism

area :: ??? -> Float

totalArea :: [???] -> Float
totalArea shapes = sum (map area shapes)

Defining a data type

  • Int
  • Float
  • String

Defining a data type

data Shape = ...
           deriving (Show)

Defining a data type

data Shape = Circle
           deriving (Show)

Shapes> let a = Circle
Shapes> a
Circle
Shapes> :t a
a :: Shape
Shapes> :t Circle
Circle :: Shape

Add a radius to the Circle

data Shape = Circle Float
           deriving (Show)

Shapes> :t Circle
Circle :: Float -> Shape
Shapes> let a = Circle 15
Shapes> a
Circle
Shapes> :t a
a :: Shape

Python Equivalent

class Shape:
  def __init__ (self, name, *args):
    self.shape = name
    self.args = args

  @classmethod
  def Circle (cls, radius):
    return cls("Circle", radius)

Add a few more shapes

data Shape = Circle Float
           | Rectangle Float Float
           | Triangle Float Float
           deriving (Show)

Shapes> :t Circle 15
Shape
Shapes> :t Rectangle 15.0 16.0
Shape

Python Equivalent

class Shape:
  def __init__ (self, name, *args):
    self.shape = name
    self.args = args

  @classmethod
  def Circle (self, radius):
    return cls("Circle", radius)

  @classmethod
  def Rectangle (self, length, width):
    return cls("Rectangle", length, width)

  @classmethod
  def Triangle (self, height, base):
    return cls("Triangle", height, base)

Destructuring

area :: Shape -> Float
area (Circle ...) = ...
area (Rectangle ...) = ...
area (Triangle ...) = ...

Destructuring

area :: Shape -> Float
area (Circle r) = pi * r ^ 2
area (Rectangle l w) = l * w
area (Triangle b h) = b * h / 2

Destructuring

area :: Shape -> Float
area (Circle r) = pi * r ^ 2
area (Rectangle l w) = l * w
area (Triangle b h) = b * h / 2

totalArea :: [Shape] -> Float
totalArea shapes = sum (map area shapes)

Seen in action

*Shapes> map area [Circle 1, Rectangle 1 1, Triangle 1 1]
[3.1415927,1.0,0.5]
*Shapes> map area [Circle 15, Rectangle 14 20, Triangle 15 16]
[706.85834,280.0,120.0]

Let’s switch to graphics

Gloss

display :: Display -> Color -> Picture -> IO ()

Gloss Pictures

data Picture = Circle Float
             | Polygon Path
             | Blank

display (InWindow "Women Who Code" (200, 200) (10, 10)) black (Circle 80)

Gloss Pictures

data Picture = Circle Float
             | Polygon Path
             | Blank
             | Color Color Picture

display (InWindow "Women Who Code" (200, 200) (10, 10)) black (Color white (Circle 80))

Gloss Pictures

data Picture = Circle Float
             | Polygon Path
             | Blank
             | Color Color Picture
             | Pictures [Picture]

display (InWindow "Women Who Code" (200, 200) (10, 10))
        black
        (Pictures [ Color white (Circle 80)
                  , Color blue (Circle 40)
                  ] )

Gloss Pictures

data Picture = Circle Float
             | Polygon Path
             | Blank
             | Color Color Picture
             | Pictures [Picture]
             | Translate Float Float Picture
             | Rotate Float Picture
             | Scale Float Float Picture

display (InWindow "Women Who Code" (200, 200) (10, 10))
        black
        (Pictures [ Color white (Translate 15 0 (Circle 80))
                  , Color blue (Circle 40)
                  ] )

References

Configuring your Haskell application

January 01, 0001

One way or another, you are going to need to configure your Haskell application, and for that you have three major ways of doing it. I recommend choosing one and sticking to it. You can choose multiple ones, but it is important that you minimize one of them in order to keep yourself out of the mind-numbing tedium of consistently combining multiple input parameter sets and their overrides.

Your options tend to be…

  • CLI Option parsing

    I recommend this for small utilities, especially those which you are going to run frequently and with a variety of configurations.

  • Configuration files

    This is generally my preferred way of running an application. You’ll still need to do a little bit with option parsing, but only enough to get a configuration. However, it can be a total pain to need to edit a file to change the configuration for a utlity, so use this for your longer-running applications.

  • Environment variables

    This is not generally how I want to configure an application, but some environments, such as Heroku, make it the easiest way.

CLI Option Parsing

The most important rule of parsing options from the CLI is…

*Don't write your own CLI parsing library.*

I have made this mistake. It is no longer on the internet. Do not do what I have done. Do this instead.

For particularly simple parameter parsing, you don’t need any libraries. For example I have a tool that I use on occasion to reformat an m3u playlist for my phone. Rhythmbox exports the playlist in an m3u format, but with all paths that don’t work for my Android phone. A tool like this is so simple that the only parameters to it are the input file and the output file.

In fact, the tool is so simple that it may have been better for me to accept the input data on standard in and emit the output data on standard out. Please forgive me for that, too.
import           System.Environment (getArgs)


main :: IO ()
main = do
    (source:dest:_) <- getArgs

That is the simplest way. However, you may wish to be kind to your users…

main :: IO ()
main = do
    args <- getArgs
    case args of
        (source:dest:_) -> {- do your thing! -}
        _ -> print "Run the application with the source and destination files."

This is your standby for applications with very simple parameters, and these applications are quite common. However, more complex configuration is often needed. For that, resort to Optparse-Applicative. This will give you command line options that are very similar in power to the one available in Go.

The tutorial covers basically everything, but here’s a starter example:

cliParser :: Parser Config
cliParser = Config <$> option auto (long "interval" <> help "number of seconds between samples" <> value 5)
                   <*> strOption (long "log" <> help "log output file")
                   ...

main = do
    Config{..} <- execParser (info (helper <*> cliParser)
                             (fullDesc <> progDesc "description of the program"))

Look here for a summary of the functions and typeclasses involved above. The entire block around execParser is basically boilerplate code, and all of the interesting bits happen inside cliParser.

This technique is as common as mud. As an administrator, I do like to pass parameters to my applications, but I dislike services that require excessively long command lines to run. If your application requires more than four or five parameters, or if the parameters rarely change from one run to the next, look to the next section for configuration files, instead.

Configuration Files

For almost all of my configuration needs, I like to go with a file on the disk. I usually put it into a Yaml format, because that allows some complex nested configurations and saves me from needing to write a configuration parser myself.

For my example, I will demonstrate with a program that I use for my HDR processing toolchain. The program has to go through several steps, and basically it needs these parameters:

  • Do I need to align the photographs?
  • What are my input files?
  • What white balance parameters should I use for developing the files?

and so forth. These are the most important parameters. A typical file looks like this:

wb: camera
project: lake-travis-dam
sources:
- _DSC3656.dng
- _DSC3657.dng
- _DSC3658.dng
- _DSC3659.dng
- _DSC3660.dng
align: false
fanout: false

So, first I want a data structure to store this:

data WhiteBalance = Camera | Auto

data Project = Project {
      sources :: [String]
    , project :: String
    , wb :: WhiteBalance
    , align :: Bool
    , fanout :: Bool
    }
    deriving (Show)


instance Default Project where
    def = Project [] "" Camera False False

(incidentally, I like having defaults for my structures, if I can concieve of a reasonable default)

Whether Yaml or JSON, in Haskell I need a FromJSON instance for parsing this file:

instance FromJSON Project where
    parseJSON (Object obj) =
        Project <$> obj .: "sources"
                <*> obj .: "project"
                <*> obj .: "wb"
                <*> obj .: "align"
                <*> obj .: "fanout"
    parseJSON obj = fail $ show obj

instance FromJSON WhiteBalance where
    parseJSON (String str) =
        case str of
            "camera" -> pure Camera
            "auto" -> pure Auto
            _ -> fail $ "invalid wb string: " ++ T.unpack str
    parseJSON (Object obj) =
        WhiteBalance <$> obj .: "temp"
                     <*> obj .: "green"
    parseJSON obj = fail $ show obj

aside: I use fail instead of mzero or mempty because propogating out any error message at all helps immensely with debugging. I wish I could use throwError, but MonadError is not implemented for Parser.

-- now include code for reading JSON format and Yaml format

Environment Variables

While I do not particularly like using environment variables for configuration an application, Heroku and presumably some other services require their use. On the other hand, most languages treat environment variables as a simple dictionary, making them simple to retrieve. Haskell is no exception to this. The only catch is that nested structures require a little more effort to build.

Your workhorse function is System.Environment.getEnv :: String -> IO String. The function will return the value if present, or throw an IO exception if it is not present. Since you may sometimes want to make the variable optional, so, here is a function that will capture isDoesNotExistError and translate it into a Maybe:

maybeGetEnv :: String -> IO (Maybe String)
maybeGetEnv k = (Just <$> getEnv k) `catch` handleIOExc
    where
    handleIOExc exc
        | isDoesNotExistError exc = pure Nothing
        | otherwise = throw exc

Then write your configuration function like so:

import Data.List.Split (splitOn)

loadConfiguration :: IO Config
loadConfiguration = do
    p <- getEnv "PROJECT_NAME"
    s <- splitOn "," <$> getEnv "SOURCES"
    align <- maybe False read <$> maybeEnv "ALIGN_IMAGES"
    fanout <- maybe False read <$> maybeEnv "FANOUT_EXPOSURES"
    pure $ Config s p Camera align fanout

These are your three major methods for configuring an application. Many applications will permit a certain degree of hybridization between them, but I think it is best to minimize that as much as possible. For instance, a command line parameter to specify the path to a configuration file. Doing it in the general case, handling command line parameters, defaults, configuration options, and environment variables, has typically lead to a very difficult-to-use mess, and I have regretted such attempts.

Whichever method you use for passing configuration in, you’ll then want to wrap that configuration up into a context for your application. I will hint more on that in my next article, on the application monad, and give it significantly more detailed treatment later on.


Questions? Comments? Feedback? Email me. I am particularly interested in places that you feel are unclear or which could use better explanation, or experiments you have run that turned out better.

Creative Commons License
Configuring your Haskell application by Savanni D’Gerinel is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.