16 Years

Savanni D'Gerinel 1 Jan, 2017

Welcome to 2017!

In a few days I move away from Austin, likely never to return. I grew up in Round Rock. I went away for a few years, and then I returned here to begin my career and my adult life right during the dot-com crash. I’m actually a lot older than I look. Many people upon meeting me seem to assume that I’m 27 or so, when in fact I turned 38 late this year. “Wait, how old are you?”

I keenly feel the passage of time. I feel that I have not begun to approach what I wanted to accomplish by now. But, realistically, I have between 40 and 60 years left. The amount of time that I spent here… three more times. And a lot can change in 16 years.

16 years ago, I thought I was a straight man. I was married. I voted for Bush and thought the Republicans could run the country well. I was Catholic and believed the anti-abortion rhetoric, yet I somehow rejected the anti-gay rhetoric. Go figure. Though we knew a few gay men in college, it was shortly after we moved to Austin that my wife and I noticed for the first time pairs of men openly holding hands at formal “respectable” events. We began to feel a relief that this was the kind of safe city that we never really recognized we sought.

15 years ago, my wife and I decided to have a polyamorous relationship. She said that I had suggested it years earlier while we were dating. I did not remember saying that, but it felt like the kind of thing I might have. It was shortly after this, as I thought about love, romance, and relationships, that I began to believe that it was tragic that I was straight and not bisexual. I can remember being apologetic as I (very occasionally) turned a man down. And it was shortly after this that I understood that I was parting ways with the Catholic church… and I did not particularly regret that.

14 years ago, I found out how infidelity felt. Infidelity in a polyamorous relationship looks different than in a monogamous relationship, but it hurts the same. It cuts through hearts, rips out rugs, and crushes dreams.

I also learned that maybe the Republican party was actually made up of a bunch of chronic liars, and became a Democrat. Later I started to understand how violent and hateful Republicans could be. How did I never see this before? And maybe, just maybe, I shouldn’t hold the reproductive health doctrines of men who want to ban abortion but also ban all other forms of contraception and all forms of sex that carry no chance of pregnancy while simultaneously starting a war and lying to me about weapons of mass destruction!

12 years ago I joined a company that became my career for the better part of a decade. They weren’t great… in fact sometimes they were downright awful, but over time my authority became vast, as did my knowledge of everything about the business… except what was in the best interest of the business. Ya know, sometimes we techies need to be informed of the big business direction so we can make decisions intelligently.

10 years ago, with the onset of Saturn Returns, I finally figured out that I was not a man. That moment has lead me through so many changes and to so many of the people that I find so important in my life now. As a man, I would never have made any of the connections I have as an androgyne. This realization sometimes keeps me awake at night, knowing that it is by the grace of but a few words that I have in my life the love that I experience now. More rationally, a few of my current friends would have been my friends anyway, and they would have noticed my egg tendencies, and they would have aided in my hatching. I may have ended up exactly where I am now, on a different schedule.

Letting go of my own gender also let me release my expectations about my sexual orientation. Reparative therapy, especially religious-based “therapy”, is bullshit. We know this. And yet, I successfully “prayed the straight away”!

I also gave up on “til death do we part” and let my marriage end.

Five years ago, I learned photography, and I changed how I see the world. Always watching for that perfect moment. Seeing textures. Analyzing light. Understanding focus and freezing motion. The speckled shadow beneath a canopy. The shimmer of a cobweb five meters up and at least that far away.

Three years ago, I talked myself out of my first suicide attempt. In the aftermath, I evaluated my life. I saw clearly how I was wasting it on my employer’s amazingly small dreams, and I chose to spend some time quite alone. I loved living out in the woods. I hated having to drive for twenty minutes to reach the closest decent internet connection, and for an hour to reach any of my friends. But there is a lot to be said for the peace of the forest, for stars so bright as to light the ground, for rain on the metal roof a mere meter from my lofted bed… and for really cheap rent paid in cash under the table. Oh, and did I mention that my landlady also covered electricty? Pretty epic, especially since the cabin wasn’t well insulated and I had to run 2.5kW of heating that winter.

In the last two years, I have truly started to learn how black lives matter, and how little I understood my own racism in the past. I have learned about social justice, and become keenly aware of my failings. I have gained true confidence in my skills, and become comfortable in my body for the first time in my life. I have felt my socialist/anarchist heart begin to blossom as I notice the Democratic party repeatedly snatch defeat from the jaws of victory.

And, shortly after my birthday in 2014, I met the woman who has become the love of my life. She had to exercise both persistence and patience. I was wounded and avoiding romance, sex, dating. She had to convince me that a lesbian, even a trans-friendly lesbian, could be interested in an androgyne who still had and wasn’t particularly inclined to get rid of eir penis. But, she exercised that persistence, and she waited patiently, while over the course of months I fell in love and I healed. Now we talk of our sixty year plan.

I will miss Austin. I will miss the people here. I will miss all of my bike routes and the restaurants and the events. I will miss the familiarity. And I feel guilt, leaving all of you to stay and stand against the legislature.

But for this woman, where she goes my heart shall follow.

Nix Development Environments

Savanni D'Gerinel 28 Nov, 2016

nix-shell, the command that creates a subshell after evaluating any nix expression, has a lot of uses. I found it very useful in my devops work when I had multiple environments to administer, but had to use different tools for each. The shell provides excellent help in isolating my required tools to the environments involved.

A trick, though, lay in learning how to acquire those tools when the tools were not available in the nixos channel. I figured it out, and so here is the example for one of the environments I was administering. Note that I include both Linux and Darwin builds, because I wanted to offer the nix environment to my replacement at the company.

  • Packer – 0.10.1
  • Terraform – 0.7.4
  • Ansible 2
  • Python 2.7

We were deploying in Amazon AWS. I used Packer to build the custom images that we were deploying. Autoscaling works a lot better if it has a complete image that only has to be started (the Crops, i.e., the systems that can be replaced almost instantly and thus do get replaced regularly). I love Terrafrom because I was able to describe everything I was doing in AWS using the tool. Ansible is present for those systems that get reconfigured regularly (primarily the Cattle machines, things that can be rebuilt from just the devops scripts, but that I do not want to terminate). Python 2.7 is present to support Ansible, though it is sometimes convenient to have at hand.

Neither Packer nor Terraform were available in my Nix channel, so I had to build derivations for those. The process is non-obvious until it is done. Here are my scripts for them. At the time I wrote these scripts, I was running NixOS 16.03, however I still use the same scripts after having upgraded to NixOS 16.09.

nix-deps/packer.nix

{ pkgs ? import <nixpkgs> {},
  stdenv ? pkgs.stdenv }:

let
  # suggestion from @clever of #nixos
  package =
         if stdenv.system == "x86_64-linux" then "packer_0.10.1_linux_amd64.zip"
    else if stdenv.system == "x86_64-darwin" then "packer_0.10.1_darwin_amd64.zip"
    else abort "unsupported platform";
  checksum =
         if stdenv.system == "x86_64-linux" then "7d51fc5db19d02bbf32278a8116830fae33a3f9bd4440a58d23ad7c863e92e28"
    else if stdenv.system == "x86_64-darwin" then "fac621bf1fb43f0cbbe52481c8dfda2948895ad52e022e46f00bc75c07a4f181"
    else abort "unsupported platform";
in
stdenv.mkDerivation rec {
  name = "packer-${version}";
  version = "0.10.1";

  buildCommand = ''
  mkdir -p $out/bin
  unzip $src
  mv packer $out/bin/packer
  echo Installed packer to $out/bin/packer
  '';

  src = pkgs.fetchurl {
    url = "https://releases.hashicorp.com/packer/0.10.1/${package}";
    sha256 = checksum;
    name = package;
  };

  buildInputs = [ pkgs.unzip ];
}

nix-deps/terraform.nix

{ pkgs ? import <nixpkgs> {},
  stdenv ? pkgs.stdenv }:

let
  # suggestion from @clever of #nixos
  package =
         if stdenv.system == "x86_64-linux" then "terraform_0.7.4_linux_amd64.zip"
    else if stdenv.system == "x86_64-darwin" then "terraform_0.7.4_darwin_amd64.zip"
    else abort "unsupported platform";
  checksum =
         if stdenv.system == "x86_64-linux" then "8950ab77430d0ec04dc315f0d2d0433421221357b112d44aa33ed53cbf5838f6"
    else if stdenv.system == "x86_64-darwin" then "21c8ecc161628ecab88f45eba6b5ca1fbf3eb897e8bc951b0fbac4c0ad77fb04"
    else abort "unsupported platform";
in
stdenv.mkDerivation rec {
  name = "terraform-${version}";
  version = "0.7.4";

  buildCommand = ''
  mkdir -p $out/bin
  unzip $src
  mv terraform $out/bin/terraform
  echo Installed terraform to $out/bin/terraform
  '';

  src = pkgs.fetchurl {
    url = "https://releases.hashicorp.com/terraform/0.7.4/${package}";
    sha256 = checksum;
    name = package;
  };

  buildInputs = [ pkgs.unzip ];
}

The structure of each script is relatively straightforward.

  • declare that pkgs and stdenv are both required, as well as how to get them if they are absent
  • based on the OS, declare what package I want to download and the relevant checksum
  • declare the name and version of the derivation
  • create the custom build command

    In many cases, the default build commands works perfectly, but that only works for projects that have to be built with autoconfig or with Stack (and possibly some other languages). Both Terraform and Packer are binaries, and so it is necessary for me to specify the build for the derivation.

    In this case, the build is simply to unzip the downloaded package (specified in $src) and copy the executable into the destination (which has a root at $out). It is vital that the executable end up in the bin/ directory. I am not sure of the mandated directory structure of a derivation, but I know that derivations that did not include the bin/ directory would fail. I assume that they failed because there was no executable to add to the path.

  • specify precisely how to get the source package. In this case, through the fetchurl tool.
  • specify additional build inputs. These have to be somewhere in the nix namespace. pkgs.unzip refers to nixpkgs.unzip in the standard channel.

Both of the files above must go in a subdirectory. I named the subdirectory nix-deps/. Some subtle interaction will cause an infinite recursion if the two files are included in the root directory of your project.

With those present, it is time to build the nix-shell command:

shell.nix

let
  pkgs = import <nixpkgs> {};
  stdenv = pkgs.stdenv;
  terraform = import nix-deps/terraform.nix {};
  packer = import nix-deps/packer.nix {};

in stdenv.mkDerivation {
  name = "v2-devops";

  buildInputs = [ pkgs.ansible2
                  terraform
                  packer
                  pkgs.python
                  pkgs.python27Packages.alembic
                  pkgs.python27Packages.boto
                  pkgs.python27Packages.psycopg2
                  pkgs.awscli
                ];
}

The only difficult part here was for me to figure out how to import my Terraform and Packer derivations. I handle that with the import nix-deps/<package>.nix {} lines. The result of each import statement is a derivation, and so it is valid to include in buildInputs.

buildInputs again just lists the packages that must be included in this derivation. So, I included all of the packages that I use directly.

Thus, from the root directory of my devops folder, I can simply run nix-shell and have exactly the version of Terraform, Packer, Ansible, and Python that I want. This also means that I can have completely different versions for a different devops repository (I was actually administering three different clouds, all with different standards). And, possibly best of all, if I could convince my co-workers to use Nix (the tool, not the operating system), they would have had a trivial way to set up their development environments, also.

Androgyne with Surgery

Savanni D'Gerinel 2 Nov, 2016

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

Computer Graphics in Haskell

Savanni D'Gerinel 24 Oct, 2016

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

Washburn, November 2005 - September 30, 2016

Savanni D'Gerinel 1 Oct, 2016

_DSC7215.web

_MG_1313

How does one speak of the departure of a cherished friend?

My dear, loyal friend for almost eleven years passed beyond at 1:40 am on Friday, September 30th. He spent the last five hours of his life in a hospital. I had thought that I was taking him in for a dangerous but curable condition. Instead, I took him in to discover that he had a creeping condition, largely undectable, that was going to kill him.

Kylie and I were with him when he went. Kat, his original human mother, made in only a few minutes later. I had hoped he would hold on just a bit longer.

Washburn came into my life with his sister River, two brothers Simon and Malcolm, and his mother Serenity, when he was a mere five weeks old. Kat and I adopted out all of the others, but opted to keep Washburn, and then when we separated I kept him along with Ghost.

IMG_20151224_091903

Washburn has slept with me almost every night of the last ten years. I cannot dream of counting the days in which I woke up to find him right there. Or nights in which I would be going to sleep and he would choose that moment in which I most needed to give him scritches. I would go to sleep with his purr and wake up to his purr. In his youth, he would investigate every open glass, first with his face and then with his paws. No glass was safe. Books were ruined for the sake of his love for water. I eventually foiled that by moving to using a water bottle almost exclusively. He spent years drinking from the faucet, often while I was brushing my teeth. He stopped and never really restarted, though in the last year I always got the impression he was interested.

20160703_185927

_DSC6988

Most of his life he was skittish around new people. In the last few years he became braver, willing to come out, investigate, and even accept pets from a new person in the house. Though he rarely had a playmate after Kat and I separated and Vladimir went with her, in the last few years he and Sapphira finally formed a bond in which they would snuggle and groom. He learned to crawl up onto my chest at night to get more pets. He would never sleep there, but sometimes he would settle down for a few minutes with his aggressive purr.

_DSC1857.web

We lived out in the woods for a year, and so he is one of the few cats who has gotten to run out in the woods. He learned the joys of climbing a tree which had branches in near reach. Of running along a branch to see the world from high up. Of discovering what his claws were really good for.

download_20150523_095724

In his last few minutes, I wanted more than anything to pick him up, lie down, and wrap him up next to my chest. To let him die listening to my heartbeat, as he was born listening to his feline mother’s heartbeat. Alas I did not. I feared it would distress him. I feared it would hasten the end. And perhaps, because of that, my heart is broken a little more.

Washburn made an impression upon everyone. Everyone who knew him is sad to see him go. Even his vet, who saw him once a year, called me, heartbroken.

I promised him I would be with him his entire life. Promise fulfilled. What more can a mother do?

_DSC5854

_DSC5521

My love, my dear friend, my child, Washburn:

I wish for you wild open fields in which to run
Forests in which to climb
Water to investigate and pet
Infinite hands to pet you
Beds in which to cuddle
And more love than even I could provide

Please forgive me for our fights
For the times I neglected you
Please remember the love I gave
The times we spent playing
The times we spent cuddled

Watch over your sisters
And be free

Maybe there is an afterlife, and maybe my love will let me visit you again.

I miss you. I will never forget you. For as long as I live, I will never let you be forgotten.

_DSC4303


I will reserve the rest of the space for stories which I will tell as I recall them, and additional pictures as I develop them.

IMG_1033

washburn_in_sink

wash01

IMG_1059

IMG_20150824_141225


Dreamer, Shaper, Seeker, Maker