Planet Haskell

August 17, 2017

Manuel M T Chakravarty

Moving On

I am excited about my new role at Tweag I/O! Curious why? Read why I am a functional programming evangelist.

August 17, 2017 12:14 AM

Tweag I/O

Diversity through inline code

Manuel M T Chakravarty

Haskell is an awesome language, but we need to remember that it is not very useful in isolation. In almost any realistic application, Haskell has to coexist with other languages, even if only to call existing C libraries or to make use of operating system services. In actual practice, the more easily we can fit Haskell into existing ecosystems, the more application domains we can unlock.

Beyond bridging

The core of Haskell’s ability to interoperate, the ForeignFunctionInterface language extension, has been available and stable for a long time. However, for all but the simplest interoperability requirements, it tends to be tedious to use. So we have tools such as hsc2hs and c2hs to automate some of the work of declaring foreign entities and writing marshalling code. This does not just save work, it also prevents many common mistakes.

Over time, we realised that this is not sufficient either. Tools like hsc2hs and c2hs are typically used to implement, what I like to call, bridging libraries. These libraries wrap the API of a foreign library in Haskell, usually by exposing a Haskell API that is close to the original and, occasionally, by providing a more functional, more high-level API. This works fine up to a certain API size. After that — just think about the API surface needed to write Android, iOS, macOS, or Windows apps — the overhead of bridging libraries tends to weigh down and break that bridge:

  • The initial implementation is a huge undertaking, which few people, or institutions, are willing to embark on.
  • API evolution of the foreign library creates a significant ongoing maintenance burden. Even worse, typically, multiple versions need to be supported simultaneously.
  • Documentation becomes a major headache. It is infeasible to transliterate all of the original documentation, but referring Haskell users to the original requires to exactly mirror that original API and demands an understanding of the bridging conventions by the library user.
  • Even just the overhead of linking all the bridging code starts to be an issue for large APIs.

Inline foreign code in Haskell sidesteps these issues. The effort to implement an inline library for a foreign language is fixed and supports an arbitrary number of foreign language libraries of arbitrary size without any further overhead. Documentation is naturally just the original and marshalling overhead is in proportion to its use in any single application. Admittedly, a user now needs to know both Haskell and the foreign language, but, given the documentation issue, that was always the case for large APIs.

I have illustrated this in a talk at the 2014 Haskell Symposium, where I introduced language-c-inline to use Objective-C code inline in Haskell to code against macOS APIs. You can watch the talk on YouTube.

I was not alone

What I didn’t know at the time is that Mathieu Boespflug, Alexander Vershilov, and Facundo Domínguez drew the same inspiration as I did from Geoff Mainland’s work on Quasiquoting Support for GHC and independently developed inline-r, a Haskell library for inline R code. Subsequently, Mathieu worked with Francesco Mazzoli on inline-c and developed inline-java with Alp Mestanogullari and Facundo Domínguez.

The latter is where foreign inline code, once again, provides an unorthodox solution to an old problem. In an attempt to fit into the ubiquitous Java ecosystem, there has been a string of failed attempts to compile Haskell to JVM code — although, maybe, one will eventually be successful, even if at a steep price. Integration with Java is highly attractive as it opens the door to many applications and commercial opportunities. In addition, successful entrants into the JVM ecosystem, such as Scala and Clojure, suggest that generating JVM bytecode is the opportune approach.

Come as you are

The fundamental design philosophy behind inline code is to accept that there are multiple language environments and work with that. We don't try to shoehorn the semantics of one language into the other. We don't even force a particular style for calling such functions. Indeed you can call Haskell, R, C, Objective-C or Java functions with each of their respective syntaxes. In some cases, this has crucial consequences. For instance, R has special syntax for variadic functions with labeled arguments and default values, none of which Haskell has. inline-r lets you e.g. call R's plot() function the way it was meant to be called:

H> let xs = [x :: Double | x <- [1..10]]
H> let ys = [x^2 | x <- xs]
H> [r| plot(xs_hs, ys_hs, col = "red", main = "A quadratic function") |]

Highly versatile functions with multiple modes of use would otherwise require multiple bindings to the same function Haskell side or unnaturally packing arguments into lists. Likewise, Objective-C has special syntax for sending messages to objects. It's convenient to be able to reuse documentation snippets that use this pervasive idiom.

But this design philosophy has further consequences still. All of the projects you saw mentioned above let each language not just keep their syntax, but also their runtimes. GCC or LLVM compiles C and Objective-C to native code. The reference R interpreter parses and executes R code on-the-fly. Java code gets compiled to JVM bytecode, using configuration extracted from Java build tools (Gradle, Maven, ...). And yes, it is GHC that compiles Haskell to native code, like it always did. To run Haskell on JVM based enterprise middlewares, we package it up as a JAR and load the native code into the JVM dynamically.

You could take the motto to be "each language, come as you are". There are tradeoffs to this approach. The bad news is that,

  • when multiple compilers are involved, we have to mesh multiple build toolchains together;
  • when multiple garbage collectors are involved, we need to make sure live data in one language is not considered garbage in the other. This is a tricky problem in general.

But on the flip side:

  • We spare ourselves implementing new code generators, e.g. for the JVM. The many failed attempts attest to the difficulty of this endeauvour.
  • By generating JVM bytecode, you lose access to all existing packages that depend on foreign code, such as C libraries. In contrast, inline-java happily enables projects involving Haskell, Java, and C without any need to change existing packages.
  • Each language is an equal citizen and the semantics and runtime behaviour of each is preserved. Note in particular that the runtime characteristics of Haskell code are not all that well matched with those that the JVM is optimised for. Haskell has a much higher allocation rate than Java, it has entirely different update patterns due to purity and laziness, and it relies on different control flow, including heavily reliance on tail calls and their optimised implementation. In this case, inline-java just uses the tried and tested GHC native code as is.

Each language is its own first-class citizen, but that's not to say each language forces a particular way to package things. We can still maintain the convenience of bundled distribution, as the Java archive (JAR) format is sufficiently flexible to allow arbitrary native code alongside JVM bytecode in a single self-contained bundle — we detailed this in a previous post on the Haskell compute PaaS with Sparkle. Conversely, we also routinely embed Java bytecode in Haskell binaries packaged as RPM's.

Happy coexistence

All in all, inline foreign code enables diversity in the form of scalable mixed language projects while requiring no more than a limited toolchain maintenance burden. Interestingly, all Haskell foreign inline code libraries have enabled and been driven by concrete applications. The Haskell for Mac IDE is built on language-c-inline. The package inline-r was originally developed for Amgen as part of a commercial project. The package inline-c was developed by FP Complete to more easily bind large mathematical libraries and is used by LumiGuide for their OpenCV work. inline-java is maintained by Tweag I/O and features contributions from Leap Year and other companies that use it at the core of their products.

August 17, 2017 12:00 AM

August 16, 2017

Michael Snoyman

Posture

About five years ago, I decided to start working out at home since I wanted to get in better shape. About three years ago, I got more serious about it as I realized my health was slipping (specifically, recurrence of asthmatic symptoms after 20 years of being clear). But I only started weight lifting 1.5 years ago, and the reason was simple: back pain.

Like many people in our industry—our industry being the "sit in front of a computer all day" industry—I suffered from chronic lower back pain. I'd been having problems with it on or off since I was a teenager (yeah, I was sitting in front of a computer then too). But over the preceeding few years, it got significantly worse. I had many episodes of waking up unable to get out of bed without significant pain. I had a few cases of my spine turning S-shaped for days on end, unable to stand up straight at all.

I have a strong family history of back pain. Like going bald, I'd taken it as a given for many years that this would happen. I went to an orthopedist, who prescribed painkillers. And that could have been the rest of my life: regular pain, popping pills, waiting to see if I'd kill my liver with the pills before something else got me. And most likely, inactivity due to back pain could have led to plenty of other health problems.

Today is a different story. I won't claim that I'm totally back pain free—problems still crop up from time to time. But the debilitating level I had previously is gone. And when some negative event occurs (like getting knocked down and back slammed by a wave this Sunday), I'm far more resilient to the damage. I'm writing this blog post since I strongly believe many of my friends, family, colleagues, and general fellow programmers suffer terribly from back pain, when they could greatly improve the situation. I'll tell you what I've done, and what I continue to do.

If you suffer from back pain, I strongly recommend you consider being proactive about it. Feel free to take my experiences into account, but also do your own research and determine what you think is your best course of action. There is unfortunately—like most things in the health world—quite a bit of contradictory advice out there.

Two pronged approach

From my research, I decided that there were likely two things I could do (outside of pill popping) that I could do to improve the situation with my back:

  • Improve the muscles in my posterior chain (lower back, glutes, hamstrings) to better support my spine
  • Change the way I was moving my back, though I didn't really understand yet how

The first bit is easy to explain. I'd been doing bodyweight workouts at home until then, which—according to the program I was following, don't really offer a good alternative to the deadlift for posterior chain work. That's why I switched to Stronglifts 5x5 and put a large emphasis on the deadlift, also focusing on stabilizing my core a lot during the squat.

I'll be honest: I threw my back out badly a few times on the squat. I almost gave up. I'm glad I didn't. I (finally) figured out how I was misusing my back on the exercises, and now can squat and deadlift almost twice the weight that had previously thrown my back out. I consider it a huge success.

In addition to the muscle improvements, the other takeaway is: lifting weights taught me how to use my back in a safer way.

Posture

But now on to the (for me) more complicated bit. I watched tons of YouTube videos, read articles, browsed forums, and spoke with doctors and chiropractors about proper posture. The problem is that there are different schools of thought on what it means to stand or sit correctly. From my reading, the most contentious point comes down to pelvic tilt. To demonstrate visually:

Pelvic tilt

There's a basic question: should your pelvis tip slightly forward, slightly backwards, or be neutral (perfectly vertical). As far as I can tell, the most mainstream opinion is a neutral pelvis. I'm always nervous to give anything close to health advice, especially contrary to mainstream opinion, so instead I'll say: I found a lot of success with the Gokhale Method, and specifically Esther's book "8 Steps to a Pain Free Back."

The reasoning Esther uses to arrive at her conclusions is solid to me. Analyzing the shape of the vertebrae, and specifically the L5-S1 joint, does make a good case for the pelvis needing to be slightly anteverted. In addition, I buy her argument of the source of back pain being the predominance of slouching introduced in the western world in the earlier 20th century. The evidence of more uniform posture among cultures unexposed to this slouching epidemic, and their relative lack of back problems, is compelling.

I won't try to describe the method here; her book and YouTube videos do a better job than I ever could. I will, however, comment on some of the takeaways that I try to keep in mind throughout the day:

  • Keep the spine in a stretched position as much as possible
  • Stack the bones: try to ensure that your weight is being distributed down your spinal column, through your pelvis, and down your legs, instead of relying on your muscles or (worse) joints to keep you stable

Keep in mind that this is not an overnight change. You'll need to practice better posture and get it to the point of muscle memory. I think it's worth every second of investment you can give it. It's not worth living your life in pain, afraid to move, and constantly doped up.

Why now?

Two things happened this week that made me want to write this blog post. I took my kids to the beach on Sunday, and as I mentioned above, got knocked down hard by a wave, which twisted my back in a bad angle. For the next few seconds that I was under water, absolute fear went through my mind. "Oh no, did my back just go out? How am I going to drive the kids home? How will I work this week? What if one of the kids gets pulled under the water and I can't save him/her?"

The wave subsided, my feet touched the floor, I stood up... and everything was fine. I know in my bones (hah!) that that kind of impact would have put me out for a week just a few years ago. I'm sitting at my desk typing this now, after having done a deadlift session in the gym, and everything is fine.

Yesterday I took a trip to the doctor (not the topic of today's post). I sat in the patient's chair in his office, and noticed that—contrary to prior visits—I was sitting perfectly upright. I hadn't thought about it. The chair wasn't really well designed either: using the back support would have required leaning back and no longer remaining straight. It was a minor victory, but I'll take it.

August 16, 2017 09:43 AM

August 14, 2017

Wolfgang Jeltsch

Haskell in Leipzig 2017 submission deadline ahead

Let me remind you that the submission deadline of Haskell in Leipzig 2017 is this Friday. We seek abstracts of about 2 pages length on anything related to Haskell. Looking forward to your contributions. 😉

About

Haskell is a modern functional programming language that allows rapid development of robust and correct software. It is renowned for its expressive type system, its unique approaches to concurrency and parallelism, and its excellent refactoring capabilities. Haskell is both the playing field of cutting-edge programming language research and a reliable base for commercial software development.

The workshop series Haskell in Leipzig (HaL), now in its 12th year, brings together Haskell developers, Haskell researchers, Haskell enthusiasts, and Haskell beginners to listen to talks, take part in tutorials, join in interesting conversations, and hack together. To support the latter, HaL will include a one-day hackathon this year. The workshop will have a focus on functional reactive programming (FRP) this time, while continuing to be open to all aspects of Haskell. As in the previous year, the workshop will be in English.

Contributions

Everything related to Haskell is on topic, whether it is about current research, practical applications, interesting ideas off the beaten track, education, or art, and topics may extend to functional programming in general and its connections to other programming paradigms.

Contributions can take the form of

  • talks (about 30 minutes),
  • tutorials (about 90 minutes),
  • demonstrations, artistic performances, or other extraordinary things.

Please submit an abstract that describes the content and form of your presentation, the intended audience, and required previous knowledge. We recommend a length of 2 pages, so that the program committee and the audience get a good idea of your contribution, but this is not a hard requirement.

Please submit your abstract as a PDF document via EasyChair until Friday, August 18, 2017. You will be notified by Friday, September 8, 2017.

Hacking Projects

Projects for the hackathon can be presented during the workshop. A prior submission is not needed for this.

Invited Speaker

  • Ivan Perez, University of Nottingham, UK

Invited Performer

  • Lennart Melzer, Robert-Schumann-Hochschule Düsseldorf, Germany

Program Committee

  • Edward Amsden, Plow Technologies, USA
  • Heinrich Apfelmus, Germany
  • Jurriaan Hage, Utrecht University, The Netherlands
  • Petra Hofstedt, BTU Cottbus-Senftenberg, Germany
  • Wolfgang Jeltsch, Tallinn University of Technology, Estonia (chair)
  • Andres Löh, Well-Typed LLP, Germany
  • Keiko Nakata, SAP SE, Germany
  • Henrik Nilsson, University of Nottingham, UK
  • Ertuğrul Söylemez, Intelego GmbH, Germany
  • Henning Thielemann, Germany
  • Niki Vazou, University of Maryland, USA
  • Johannes Waldmann, HTWK Leipzig, Germany

Tagged: conference, FRP, functional programming, Haskell

by Wolfgang Jeltsch at August 14, 2017 10:19 PM

August 12, 2017

Dan Piponi (sigfpe)

What is a photon?


Introduction

Popular science writing about quantum mechanics leaves many people full of questions about the status of photons. I want to answer some of these without using any tricky mathematics.


One of the challenges is that photons are very different to ordinary everyday objects like billiard balls. This is partly because photons are described by quantum mechanics whereas billiard balls are better modelled with classical Newtonian mechanics. Quantum mechanics defies many of our intuitions. But it's also because the word photon plays by different linguistic rules to billiard ball. I hope to explain why.


One of my goals is to avoid saying anything original. I'm largely going remove the mathematics from material I first learnt from three or so courses I took at Cambridge University many years ago: Quantum Mechanics, Solid State Physics and Quantum Field Theory. I also learnt about some of this from David Miller at Stanford University who talked a little about what properties it is meaningful to apply to a photon. (I hope I haven't misrepresented him too badly.)



The simple harmonic oscillator


Here's a mass hanging on a spring:




Suppose it's initially sitting in equilibrium so that the net force acting on it is zero. Now we lift the mass a small distance and let it go. Because we lifted it, we shortened the spring, reducing its tension. This means the force due to gravity is now more than the spring tension and the mass falls. Eventually it falls below the equilibrium point, increasing the tension in the spring so there is a net force pulling it back up again. To a good approximation, the force restoring the mass to its equilibrium point is proportional to how far it has been displaced. When this happens we end up with oscillating motion where the mass bounces up and down. Here's what a graph of its displacement looks like over time:





It's actually a sine wave but that detail doesn't matter for us right now.


An oscillator where the restoring force is proportional to the displacement from the equilibrium point is called a simple harmonic oscillator and its oscillation is always described by a sine wave.


Note that I'm ignoring friction here. This is a reasonable approximation for many physical systems.


Masses on springs aren't all that important in themselves. But simple harmonic oscillators are very common. Another standard example is the pendulum swinging under the influence of gravity:





At a more fundamental level, an example might be an atom in a crystal being held in place by electrostatic forces from its neighbouring atoms.


If you have one of these systems, then in principle you can set it in motion with as little energy as you like. Pull a mass on a spring down a little bit and it will bounce back up, oscillating a certain amount. Pull the mass down half the amount and it'll bounce with oscillations half the size. In principle we could keep repeating this experiment, each time starting with the mass displaced half the amount we tried previously. In other words, a simple harmonic oscillator can have any energy we like. The spectrum of possible energies of one of these oscillators is continuous. (Note that the word spectrum here is merely physicist-speak for a set of possible values.) If we can set one in motion with 1 unit of energy then we can also set it oscillating with 0.5 units, or 0.01 units, or 0.000123 units of energy.



Quantum mechanics


Everything I've said above is assuming that classical Newtonian mechanics is valid. But we know that for very small systems, around the size of a few atoms or smaller, we need to use quantum mechanics. This is an enormous topic but I'm only going to extract one basic fact. According to quantum mechanics, a simple harmonic oscillator isn't free to oscillate with any energy you like. The possible energy levels, the spectrum of the system, is discrete. There is a lowest energy level, and then all of the energy levels above that are equally spaced like so, going up forever:





We usually call the lowest energy level the ground state or vacuum state and call the higher levels excited states.


The spacing of the energy levels depends on the stiffness of the system, which is just a measure of how much the restoring force increases with displacement from equilibrium. Stiffer systems will have a higher frequency of oscillation and a bigger spacing between the energy levels.


(I'm deliberately not saying anything about why we get discrete energy levels in quantum mechanics. I just want to use this one fact so I can get on and talk about photons eventually.)


In practice the difference in energy between one level and the next is tiny. This means that if you're literally fiddling about with a mass on a spring you won't ever feel the discreteness. The amount your hand trembles is many orders of magnitude greater than the effect of this discreteness. Nonetheless, it is extremely important when modeling microscopic systems.



Quantum linguistics


Here are some English sentences we could say about the kinds of systems I've described so far:


  1. This system is in the ground state.
  2. That system is in its first excited state
  3. This system is at an energy level higher than that system
  4. After allowing these two similar oscillators to interact, the energy level of this oscillator went down and the energy level of that one went up by the same amount.


Now I want to introduce the (count) noun quantum, with plural quanta. The idea here is not that I'm telling you about a new entity. I want to present this as a new way to talk about things I've already introduced. So rather than give a definition of quantum I will instead show how you can rewrite the above sentences using the language of quanta:


  1. There are no quanta in this system
  2. That system has one quantum of energy
  3. This system has more quanta than that one
  4. Some quanta were transferred from this system to that system.


Those sentences make it seem like I'm talking about a new kind of object - the quantum. But I'm not. They're just a manner of speaking about energy levels. I hope I've given you enough examples to get the idea.


Just in case you think it's weird to talk about energy levels in terms of quanta, I'd like to remind you that you already do this all the time with money. Dollar bills are actual objects that exist in the world. But money in your bank account isn't. Somewhere in some database is a representation of how much money you have. You might say "I have one hundred dollars in my savings account" But those dollars certainly don't exist as distinct entities. It doesn't really make sense to talk about the thirty-seventh dollar in your bank account. You can transfer dollars from one account to another, and yet what's really happening is that two totals are being adjusted. We treat these accounts a lot like they're containers holding individual objects called dollars. Certainly our language is set up like that. But we know that it's really just the totals that have any kind of representation. The same goes for quanta. It's just a manner of speaking about systems that can have different amounts of energy and where the spectrum of energy levels forms a ladder with equally spaced rungs. Because of your experience with money I probably don't need to give you any more examples.


One more bit of terminology: when the spectrum of energies is discrete it's said to be quantised.



Coupled systems


Let's return to classical physics with a slightly more complex system consisting of two masses connected to springs. We ignore gravity now:





We restrict ourselves to just considering back and forth motion constrained along a horizontal line. This is a coupled system. If the left mass moves to the right, not only does it experience a restoring force pushing it left, but the mass on the right will experience more of a force pushing it to the left. We can't treat the masses as independent and so we don't get the simple solution of each mass always oscillating with a sine wave.


For this particular problem though there's a trick to turn it into a pair of harmonic oscillators. The idea is to consider the pair of masses as a single entity. We can think of the motion centre of mass of the pair, the midpoint between them, as being one variable that describes this entity. Let's call its motion the external motion. We can also think of the distance between the two masses in the pair as being the system's internal motion. (I'm just using internal and external as convenient names. Don't read too much into them.) It turns out that when you analyse this using classical dynamics the internal motion and the external motion act like independent quantities. What's more, each one behaves exactly like it's simple harmonic. So we get one sine wave describing the overall motion of the pair, and another one that describes how the elements of the pair oscillate with respect to each other.


The frequencies of the internal and external motions are typically different. So you can end up with some quite complicated motions with two different frequencies beating against each other.


When we're able to find ways to split up the motion into independent quantities, each of which is simple harmonic, each kind of motion is said to be a normal mode.


When you have independent normal modes, you can treat them independently in quantum mechanics too. So what we get is that the spectrum of possible energy levels for this system is, in some sense, two-dimensional. We can put quanta into the internal oscillation and we can also put quanta into the external oscillation. Because these modes have different frequencies the quanta for each mode correspond to different amounts of energy.


(And a reminder: when talking about quantum mechanics I'm not talking literally about masses on springs. I'm talking about physical systems that have equations of motion that mean they behave like masses on springs. In this case it might be a pair of particles trapped in a microscopic well with a repulsive force between them.)



Solid state physics

Now I'm going to jump from just two masses to a large number of them. For example, the behavior of trillions of atoms in a solid crystal can be approximately modelled by a grid of masses and springs, of which the following diagram is just a tiny piece:





A real crystal would be arranged in a 3D lattice but I've drawn 2D here for convenience.


Think of the springs as both pushing apart atoms that get close, and pulling together atoms that move apart.


This is a highly coupled system. Ultimately every atom in our lattice is connected to every other one, either directly, or indirectly. Nonetheless, it is still possible to find normal modes. The normal modes all have the same basic form: they are all sinusoidal waves of displacement traveling in some direction with some speed and oscillation frequency. Each of these modes consists of waves that extend through the entire crystal, with fixed spacing between parallel planar wavefronts. This type of waves is known as a plane wave. If the system is perfectly harmonic, so the restoring force is precisely proportional to the displacement, then each direction and frequency of wave oscillates its way through the crystal completely independently of any other. Just as how in the example with two masses any possible oscillation is a combination of internal and external motion, for a crystal lattice any motion is a combination of these plane waves. (Decomposing any oscillation as a combination of plane waves is known as computing its Fourier transform.


Now we're ready to consider this situation quantum mechanically. Because each plane wave is a normal mode, we can treat each one as an independent simple harmonic oscillator. This means that the energy in each plane wave is quantised. So when we consider a crystal lattice quantum mechanically we find that its states consist of plane waves propagating through it, but where the amount of energy in each wave is given by a discrete spectrum. So again we can talk about how many quanta there are in each mode.


Linguistically it gets a bit more interesting now. Each plane wave is associated with a particular direction and speed so it makes sense to talk of these quanta as having a direction and speed. But note that statements involving quanta are still really just sentences about energy levels. So, for example, the statement "the mode of this system with this velocity and frequency is in its first excited state" is, by definition, exactly the same as "this system has precisely one quantum with this velocity and frequency". In particular, when we write sentences like these we aren't implying that there is some new kind of object, the quantum, that has suddenly attached itself to our crystal. The quanta are properties of the lattice. By the way, in the particular case of vibrating atoms in a lattice, the quanta are known by a special name: phonons.



Quantum field theory and photons

And now we're ready to move onto photons.


In classical physics, electromagnetism is described by Maxwell's equations. Maxwell's equations say that a varying magnetic field generates an electric field and a varying electric field generates a magnetic field. The result is that it is possible for an oscillating electric field to create an oscillating electric field so that an electric field can propagate through space on its own without the help of electric charges or electric currents or any other kind of `generator'. As these electric fields also produce magnetic fields that propagate with them, the whole thing is called an electromagnetic wave.


Just like displacements in a crystal lattice, an electromagnetic wave also has normal modes. The normal modes are plane waves traveling at the speed of light in a particular directions with a given frequency. You have personal experience of this. Visible light is electromagnetic radiation with a frequency of around 500 THz. Wifi uses signals at around 5 GHz. The radio might use signals at around 100 MHz. When you surf the web wirelessly while listening to the radio, the wifi signals don't interfere with your vision or the radio signal. (Actually, wifi might interfere with the radio signals, but not because of the 5 GHz signals. It might happen if badly manufactured hardware emits stray signals around the 100 MHz band.) That's because these waves pass through each other without being coupled to each other in any way. And at this point you might already be guessing what a photon is. For each choice of frequency and direction (and also polarisation, but that's just a detail) the amount of energy that can be in the corresponding mode is quantised. For the electromagnetic field the quanta are called photons.


And that's it!


Electromagnetic waves can be thought of as being made up of different oscillation modes. Because of quantum mechanics, each mode contains an amount of energy that is quantised to be a whole number multiple of some base amount. Although the thing that really matters is the total amount of energy in the modes, it can still be useful to talk about this total as if it's a collection of entities called photons.


One thing to notice is that the normal modes for an electromagnetic wave are plane waves that are extended in space. In principle all the way across the universe but for practical problems physicists often consider electromagnetic waves in a large but finite box. This means that adding a quantum to a system has an effect that extends across the entire system. That makes it problematic to talk about the location of a photon.



Caveat

Physicists sometimes use the word photon in slightly different but related ways. I've described what I think of as the core definition as presented in many courses on quantum field theory.



Acknowledgements

Thanks to @dmoore2718 for encouraging me to edit this document down to a better size.

by Dan Piponi (noreply@blogger.com) at August 12, 2017 03:22 AM

August 11, 2017

Noam Lewis

Safer C programming

TL;DR – check out elfs-clang-plugins, cool plugins for clang made at elastifile.

Have you ever made the mistake of returning a bool instead of an enum?

enum Result do_something(void) {
    ...
    return true;
}

In C that’s valid (in C++ you can use ‘class enum’ to avoid it, but if you didn’t you’d have the same problem).

No compiler that I know of warns about this C code. One of our newly-open-sourced clang plugins, flags this (and many other) enum-related mistakes:

clang -Xclang -load -Xclang ./clang_plugins.so \
      -Xclang -add-plugin -Xclang enums_conversion \
      -c /tmp/test.c
/tmp/test.c:7:12: error: enum conversion to or from enum Result
    return true;
           ^
1 error generated.

The package includes:

  • enums_conversion: Finds implicit casts to/from enums and integral types
  • include_cleaner: Finds unused #includes
  • large_assignment: Finds large copies in assignments and initializations (size is configurable)
  • private: Prevents access to fields of structs that are defined in private.h files

More information at https://github.com/sinelaw/elfs-clang-plugins

Because C is… not so safe.

 


by sinelaw at August 11, 2017 06:21 AM

August 09, 2017

Tweag I/O

Array programming in Haskell

Manuel M T Chakravarty

Array programming in Haskell is attractive, but it is also somewhat confusing. Haskell’s functional syntax is close to the mathematical notation that is often used to develop and explain the algorithms on which array programs are based, which leads to great clarity of expression. At the same time, we can execute Haskell array code with performance that is competitive with more low-level, tedious to write and debug code. That is the good news, and it is why array programming in Haskell is increasingly gaining traction.

On the confusing side, there are several different array libraries with overlapping feature sets, but distinct focus, strengths, and level of support, and often they have got widely different performance characteristics. This can make it hard to know where to start. At some point, we, as a community, need to bring some order into this plethora of options, so that others who want to use Haskell for array programming will see a clear path ahead, but that is a story for another time.

For now, I’d like to dedicate a series of blog posts to introducing you to some of the more popular options for array programming in Haskell. In the reminder of this post, we will look at the various kinds of array libraries that we have got at our disposal and what the relative strengths and weaknesses are. In subsequent posts, we will focus on individual packages and see how we can use them.

Standard routines

The simplest way to approach array programming is by relying on a set of standardised array routines with well-understood semantics, performance characteristics, and applications, such as BLAS (Basic Linear Algebra Subprograms) and LAPACK (Linear Algebra Package). In the Haskell ecosystem, this functionality is provided by the package hmatrix: Numeric Linear Algebra, which is simply a Haskell wrapper around the BLAS and LAPACK C libraries. It also has a few companion libraries to support the additional functionality found in the GNU Scientific Library and the GNU Linear Programming Kit - Wikipedia.

In addition to the CPU libraries, cublas package provides access to GPU-accelerated variants of these standard array routines. There are a few further, similar packages on Hackage, but they all don’t appear to be actively maintained.

Standard arrays

When the standard routines are not sufficient for the problem at hand, we can fall back to a range of libraries that provide basic array building blocks that we can use to implement our own array algorithms. This is where the choice starts to get overwhelming. We have the standard, immutable and lazy Haskell 2010 arrays. On top of that, the array package bundled with GHC provides a range of mutable and immutable arrays for use with boxed and unboxed data, including support for low-level, C-style, hand-tuned imperative array code. There are a few more packages that provide similar functionality, but differ in how arrays are stored and how memory is managed.

Single-core

Beyond array, we can categorise different packages by whether they are designed to support parallelism by simultaneously executing code on multiple CPU or GPU cores. The most popular choice for fast single-core arrays is the package vector: Efficient Arrays — one of the spin-offs of the Data Parallel Haskell project. The package vector produces not only highly efficient code using a range of array fusion techniques, it also supports a wide range of array flavours, including the storage of boxed and unboxed data. Due to its popularity, vector has grown its own ecosystem of interoperability and extension packages, including access to highly optimised Fourier transforms based on FFTW.

The fundamental difference between array and vector is that array provides a mostly index-based interface to the programmer, which allows for great control, but also imposes an imperative style of programming. In contrast, vector favours whole-vector processing collective operations — also referred to as wholemeal programming. This raises the level of abstractions, but it also puts a larger burden on vector and the Haskell compiler to compile this high-level code to efficient machine code.

Multicore

If we want parallelism, the choice is mostly between repa: High performance, regular, shape polymorphic parallel arrays (another spin-off from Data Parallel Haskell) and Accelerate: High-Performance Haskell (the successor to Data Parallel Haskell). Both libraries have many similarities, for example, in the support for shape-polymorphic array code, but also exhibit a fundamental difference: Repa is a standard collection-oriented Haskell library , much like vector, whereas Accelerate is an embedded array language in Haskell.

The major technical consequence out of that distinction is that Repa code is compiled prior to runtime with GHC’s standard code generator. In contrast, Accelerate code is just-in-time compiled at application runtime using a custom code generator. In fact, there are multiple code generators, which results in support for multicore CPUs as well as GPUs. This added flexibility and, in many cases, performance advantage comes at the expense of a somewhat less expressive and more involved programming model.

Beyond

In addition to the packages explicitly mentioned above, there are many experimental, incomplete, or unsupported alternatives. Some of them incorporate great ideas and some show impressive performance. However, the criteria for the packages explicitly mentioned in this post are that a package must (1) be available on Hackage, (2) be actively maintained, and (3) be used outside of the group that develops it.

Let me know what your favourite array library is and what you are using it for. In the next instalment, we will have a closer look at hmatrix.

August 09, 2017 12:00 AM

August 08, 2017

Mark Jason Dominus

That time I met Erdős

I should have written about this sooner, by now it has been so long that I have forgotten most of the details.

I first encountered Paul Erdős in the middle 1980s at a talk by János Pach about almost-universal graphs. Consider graphs with a countably infinite set of vertices. Is there a "universal" graph such that, for any finite or countable graph , there is a copy of inside of ? (Formally, this means that there is an injection from the vertices of to the vertices of that preserves adjacency.) The answer is yes; it is quite easy to construct such a and in fact nearly all random graphs have this property.

But then the questions become more interesting. Let be the complete graph on a countably infinite set of vertices. Say that is “almost universal” if it includes a copy of for every finite or countable graph except those that contain a copy of . Is there an almost universal graph? Perhaps surprisingly, no! (Sketch of proof.)

I enjoyed the talk, and afterward in the lobby I got to meet Ron Graham and Joel Spencer and talk to them about their Ramsey theory book, which I had been reading, and about a problem I was working on. Graham encouraged me to write up my results on the problem and submit them to Mathematics Magazine, but I unfortunately never got around to this. Graham was there babysitting Erdős, who was one of Pách's collaborators, but I did not actually talk to Erdős at that time. I think I didn't recognize him. I don't know why I was able to recognize Graham.

I find the almost-universal graph thing very interesting. It is still an open research area. But none of this was what I was planning to talk about. I will return to the point. A couple of years later Erdős was to speak at the University of Pennsylvania. He had a stock speech for general audiences that I saw him give more than once. Most of the talk would be a description of a lot of interesting problems, the bounties he offered for their solutions, and the progress that had been made on them so far. He would intersperse the discussions with the sort of Erdősism that he was noted for: referring to the U.S. and the U.S.S.R. as “Sam” and “Joe” respectively; his ever-growing series of styles (Paul Erdős, P.G.O.M., A.D., etc.) and so on.

One remark I remember in particular concerned the $3000 bounty he offered for proving what is sometimes known as the Erdős-Túran conjecture: if is a subset of the natural numbers, and if diverges, then contains arbitrarily long arithmetic progressions. (A special case of this is that the primes contain arbitrarily long arithmetic progressions, which was proved in 2004 by Green and Tao, but which at the time was a long-standing conjecture.) Although the $3000 was at the time the largest bounty ever offered by Erdős, he said it was really a bad joke, because to solve the problem would require so much effort that the per-hour payment would be minuscule.

I made a special trip down to Philadelphia to attend the talk, with the intention of visiting my girlfriend at Bryn Mawr afterward. I arrived at the Penn math building early and wandered around the halls to kill time before the talk. And as I passed by an office with an open door, I saw Erdős sitting in the antechamber on a small sofa. So I sat down beside him and started telling him about my favorite graph theory problem.

Many people, preparing to give a talk to a large roomful of strangers, would have found this annoying and intrusive. Some people might not want to talk about graph theory with a passing stranger. But most people are not Paul Erdős, and I think what I did was probably just the right thing; what you don't do is sit next to Erdős and then ask how his flight was and what he thinks of recent politics. We talked about my problem, and to my great regret I don't remember any of the mathematical details of what he said. But he did not know the answer offhand, he was not able solve it instantly, and he did say it was interesting. So! I had a conversation with Erdős about graph theory that was not a waste of his time, and I think I can count that as one of my lifetime accomplishments.

After a little while it was time to go down to the auditorium for the the talk, and afterward one of the organizers saw me, perhaps recognized me from the sofa, and invited me to the guest dinner, which I eagerly accepted. At the dinner, I was thrilled because I secured a seat next to Erdős! But this was a beginner mistake: he fell asleep almost immediately and slept through dinner, which, I learned later, was completely typical.

by Mark Dominus (mjd@plover.com) at August 08, 2017 09:06 PM

August 07, 2017

FP Complete

Stack Issue Triagers

First, the boring, not over-the-top version: the Stack team is starting a new initiative, the Stack Issue Triagers. We're asking for volunteers to go through the Stack issue tracker on Github and help users with support questions get moving more quickly, and alert the development team of bugs that require their attention. A more advanced version of this would be providing a dedicated IRC channel or similar forum to allow people to ask questions, though that's up for debate. By becoming an issue triager, you'll be helping the Haskell community onboard new users, get a chance to interact directly with the Stack developers, and very likely find an easier way to get onboard with being a Stack developer yourself.

But that's boring.

The time has come to seize your destiny. You've always known there was something greater waiting for you. Deep in your bones you've felt it. Your chance in nigh! You dare not miss this opportunity to jump into the fray and impact the cosmos.

Shape the future of Stack! Grow the Haskell user base! Destroy imperative programming! Stop all software bugs, ushering a new era of peace and prosperity (until Haskell starts the singularity of course).


Anyway, since this is a new, experimental initiative, we'll start this off small, but if successful hopefully we'll grow it significantly. If you're interested in participating, please fill out this form.

August 07, 2017 04:20 AM

August 06, 2017

Roman Cheplyaka

5 ways to manage allocated memory in Haskell

Let’s say we have a foreign function that takes a C data structure. Our task is to allocate the structure in memory, fill in the fields, call the function, and deallocate the memory.

The data structure is not flat but contains pointers to other data structures that also need to be allocated. An example of such a data structure is a linked list:

typedef struct list {
  int car;
  struct list *cdr;
} list;

And an example of a C function is one that computes the sum of all elements in the list:

int sum(list *l) {
  int s = 0;
  while (l) {
    s += l->car;
    l = l->cdr;
  }
  return s;
}

In this article, I will explore different ways to track all the allocated pointers and free them reliably.

The complete code can be downloaded as a git repo:

git clone https://ro-che.info/files/2017-08-06-manage-allocated-memory-haskell.git

The modules below use the hsc2hs preprocessor; it replaces things like #{peek ...}, #{poke ...}, and #{size ...} with Haskell code.

Way 1: traverse the structure

Since all pointers are stored somewhere in the data structure, we could traverse it to recover and free those pointers.

mkList :: [Int] -> IO (Ptr List)
mkList l =
  case l of
    [] -> return nullPtr
    x:xs -> do
      struct <- mallocBytes #{size list}
      #{poke list, car} struct x
      xs_c <- mkList xs
      #{poke list, cdr} struct xs_c
      return struct

freeList :: Ptr List -> IO ()
freeList l
  | l == nullPtr = return ()
  | otherwise = do
      cdr <- #{peek list, cdr} l
      free l
      freeList cdr

way1 :: Int -> IO Int
way1 n = bracket (mkList [1..n]) freeList csum

This is how we’d probably do it in C, but in Haskell it has several disadvantages compared to the other options we have:

  1. The code to traverse the structure has to be written manually and is prone to errors.
  2. Having to do the extra work of traversing the structure makes it slower than some of the alternatives.
  3. It is not exception-safe; if something happens inside mkList, the already allocated pointers will be lost. Note that this code is async-exception-safe (bracket masks async exceptions for mkList), so the only exceptions we need to worry about must come from mkList or freeList (e.g. from mallocBytes).

Way 2: allocaBytes and ContT

The Foreign.Marshal.Alloc module provides the bracket-style allocaBytes function, which allocates the memory and then automatically releases it.

-- |@'allocaBytes' n f@ executes the computation @f@, passing as argument
-- a pointer to a temporarily allocated block of memory of @n@ bytes.
-- The block of memory is sufficiently aligned for any of the basic
-- foreign types that fits into a memory block of the allocated size.
--
-- The memory is freed when @f@ terminates (either normally or via an
-- exception), so the pointer passed to @f@ must /not/ be used after this.
allocaBytes :: Int -> (Ptr a -> IO b) -> IO b

It works great if we need to allocate a small fixed number of structures:

allocaBytes size1 $ \ptr1 ->
  allocaBytes size2 $ \ptr2 -> do
    -- do something with ptr1 and ptr2

But what if the number of allocations is large or even unknown, as in our case?

For that, we have the continuation monad!

mallocBytesC :: Int -> ContT r IO (Ptr a)
mallocBytesC n = ContT $ \k -> allocaBytes n k

mallocBytesC is a monadic function that returns a pointer to the allocated memory, so it is as convenient and flexible as the simple mallocBytes used in Way 1. But, unlike mallocBytes, all allocated memory will be safely and automatically released at the point where we run the ContT layer.

mkList :: [Int] -> ContT r IO (Ptr List)
mkList l =
  case l of
    [] -> return nullPtr
    x:xs -> do
      struct <- mallocBytesC #{size list}
      liftIO $ #{poke list, car} struct x
      xs_c <- mkList xs
      liftIO $ #{poke list, cdr} struct xs_c
      return struct

way2 :: Int -> IO Int
way2 n = runContT (liftIO . csum =<< mkList [1..n]) return

This is my favorite way: it is simple, safe, fast, and elegant.

Way 3: ResourceT

If we replace ContT r with ResourceT, we get a similar function with essentially the same semantics: the memory released when the ResourceT layer is run.

mallocBytesR :: Int -> ResourceT IO (Ptr a)
mallocBytesR n = snd <$> allocate (mallocBytes n) free

The rest of the code barely changes:

mkList :: [Int] -> ResourceT IO (Ptr List)
mkList l =
  case l of
    [] -> return nullPtr
    x:xs -> do
      struct <- mallocBytesR #{size list}
      liftIO $ #{poke list, car} struct x
      xs_c <- mkList xs
      liftIO $ #{poke list, cdr} struct xs_c
      return struct

way3 :: Int -> IO Int
way3 n = runResourceT (liftIO . csum =<< mkList [1..n])

However, as we shall see, this version is an order of magnitude slower than the ContT version.

Way 4: single-block allocation via mallocBytes

Even though our structure contains lots of pointers, we don’t have to allocate each one of them separately. Instead, we could allocate a single chunk of memory and calculate the pointers ourselves.

This method may be more involved for complex data structures, but it is the fastest one, because we only need to keep track of and deallocate a single pointer.

writeList :: Ptr List -> [Int] -> IO ()
writeList ptr l =
  case l of
    [] -> error "writeList: empty list"
    [x] -> do
      #{poke list, car} ptr x
      #{poke list, cdr} ptr nullPtr
    x:xs -> do
      #{poke list, car} ptr x
      let ptr' = plusPtr ptr #{size list}
      #{poke list, cdr} ptr ptr'
      writeList ptr' xs

mkList :: [Int] -> IO (Ptr List)
mkList l
  | null l = return nullPtr
  | otherwise = do
      ptr <- mallocBytes (length l * #{size list})
      writeList ptr l
      return ptr

way4 :: Int -> IO Int
way4 n = bracket (mkList [1..n]) free csum

Way 5: single-block allocation via allocaBytes + ContT

This is the same as Way 4, except using allocaBytes instead of mallocBytes. The two functions allocate the memory differently, so I thought I’d add this version to the benchmark.

mkList :: [Int] -> ContT r IO (Ptr List)
mkList l
  | null l = return nullPtr
  | otherwise = do
      ptr <- mallocBytesC (length l * #{size list})
      liftIO $ writeList ptr l
      return ptr

way5 :: Int -> IO Int
way5 n = runContT (liftIO . csum =<< mkList [1..n]) return

Is sum pure?

We expose the sum function from C as an IO function csum:

foreign import ccall "sum" csum :: Ptr List -> IO Int

The alternative is to expose it as a pure function:

foreign import ccall "sum" csum :: Ptr List -> Int

The Haskell FFI explicitly allows both declarations, and it may seem that a function summing up numbers deserves to be pure.

However, declaring csum as pure would break every single example above (after making the trivial changes to make it type check). Can you see why?

Benchmark results

The benchmark consists of allocating, summing, and deallocating a list of numbers from 1 to 100.

<figure> </figure>
benchmarking way1
time                 3.420 μs   (3.385 μs .. 3.461 μs)
                     0.999 R²   (0.999 R² .. 1.000 R²)
mean                 3.439 μs   (3.414 μs .. 3.508 μs)
std dev              127.8 ns   (72.34 ns .. 244.5 ns)
variance introduced by outliers: 48% (moderately inflated)

benchmarking way2
time                 2.150 μs   (2.142 μs .. 2.158 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 2.142 μs   (2.135 μs .. 2.149 μs)
std dev              24.06 ns   (21.18 ns .. 28.03 ns)

benchmarking way3
time                 12.14 μs   (12.10 μs .. 12.21 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 12.22 μs   (12.17 μs .. 12.30 μs)
std dev              203.6 ns   (156.8 ns .. 277.2 ns)
variance introduced by outliers: 14% (moderately inflated)

benchmarking way4
time                 1.499 μs   (1.489 μs .. 1.509 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 1.488 μs   (1.483 μs .. 1.495 μs)
std dev              19.83 ns   (16.17 ns .. 24.72 ns)
variance introduced by outliers: 12% (moderately inflated)

benchmarking way5
time                 1.423 μs   (1.405 μs .. 1.447 μs)
                     0.999 R²   (0.998 R² .. 0.999 R²)
mean                 1.431 μs   (1.418 μs .. 1.448 μs)
std dev              51.46 ns   (41.00 ns .. 72.46 ns)
variance introduced by outliers: 49% (moderately inflated)

August 06, 2017 08:00 PM

Mark Jason Dominus

How Shazam works

Yesterday I discussed an interesting failure on the part of Shazam, a phone app that can recognize music by listening to it. I said I had no idea how it worked, but I did not let that stop me from pulling the following vague speculation out of my butt:

I imagine that it does some signal processing to remove background noise, accumulates digests of short sections of the audio data, and then matches these digests against a database of similar digests, compiled in advance from a corpus of recordings.

Julia Evans provided me with the following reference: “An Industrial-Strength Audio Search Algorithm” by Avery Li-Chun Wang of Shazam Entertainment, Ltd. Unfortunately the paper has no date, but on internal evidence it seems to be from around 2002–2006.

M. Evans summarizes the algorithm as follows:

  1. find the strongest frequencies in the music and times at which those frequencies happen
  2. look at pairs and turn those into pairs into hashes (by subtracting from )
  3. look up those hashes in your database

She continues:

so basically Shazam will only recognize identical recordings of the same piece of music—if it's a different performance the timestamps the frequencies happen at will likely be different and so the hashes won't match

Thanks Julia!

Moving upwards from the link Julia gave me, I found a folder of papers maintained by Dan Ellis, formerly of the Columbia University Electrical Engineering department, founder of Columbia's LabROSA, the Laboratory for the Recognition and Organization of Speech and Audio, and now a Google research scientist.

In the previous article, I asked about research on machine identification of composers or musical genre. Some of M. Ellis’s LabROSA research is closely related to this. See for example:

There is a lot of interesting-looking material available there for free. Check it out.

(Is there a word for when someone gives you a URL like http://host/a/b/c/d.html and you start prying into http://host/a/b/c/ and http://host/a/b/ hoping for more goodies? If not, does anyone have a suggestion?)

by Mark Dominus (mjd@plover.com) at August 06, 2017 03:31 PM

Joachim Breitner

Communication Failure

I am still far from being a professor, but I recently got a glimps of what awaits you in that role…

From: Sebastian R. <…@gmail.com>
To: joachim@cis.upenn.edu
Subject: re: Errors

I've spotted a basic error in your course on Haskell (https://www.seas.upenn.edu/~cis194/fall16/). Before I proceed, it's cool if you're not receptive to errors being indicated; I've come across a number of professors who would rather take offense than admit we're all human and thus capable of making mistakes... My goal is to find a resource that might be useful well into the future, and a good indicator of that is how responsive the author is to change.

In your introduction note you have written:

n contrast to a classical intro into Haskell, we do not start with numbers, booleans, tuples, lists and strings, but we start with pictures. These are of course library-defined (hence the input CodeWorld) and not part of “the language”. But that does not make them less interesting, and in fact, even the basic boolean type is library defined – it just happens to be the standard library.

Howeverm there is no input CodeWorld in the code above. Have you been made aware of this error earlier?

Regards, ...

Nice. I like when people learn from my lectures. The introduction is a bit werid, but ok, maybe this guy had some bad experiences.

Strangley, I don’t see a mistake in the material, so I respond:

From: Joachim Breitner <script type="text/javascript"> </script><noscript>joachim at cis dot upenn dot edu</noscript>
To: Sebastian R. <…@gmail.com>
Subject: Re: Errors

Dear Sebastian,

thanks for pointing out errors. But the first piece of code under “Basic Haskell” starts with

{-# LANGUAGE OverloadedStrings #-}
import CodeWorld

so I am not sure what you are referring to.

Note that these are lecture notes, so you have to imagine a lecturer editing code live on stage along with it. If you only have the notes, you might have to infer a few things.

Regards, Joachim

A while later, I receive this response:

From: Sebastian R. <…@gmail.com>
To: Joachim Breitner <script type="text/javascript"> </script><noscript>joachim at cis dot upenn dot edu</noscript>
Subject: Re: Errors

Greetings, Joachim.

Kindly open the lecture slides and search for "input CodeWorld" to find the error; it is not in the code, but in the paragraph that implicitly refers back to the code.

You might note that I quoted this precisely from the lectures... and so I repeat myself... this came from your lectures; they're not my words!

In contrast to a classical intro into Haskell, we do not start with numbers, booleans, tuples, lists and strings, but we start with pictures. These are of course library-defined (hence the input CodeWorld) and not part of “the language”. But that does not make them less interesting, and in fact, even the basic boolean type is library defined – it just happens to be the standard library.

This time around, I've highlighted the issue. I hope that made it easier for you to spot...

Nonetheless, I got my answer. Don't reply if you're going to fight tooth and nail about such a basic fix; it's simply a waste of both of our time. I'd rather learn from somewhere else...

On Tue, Aug 1, 2017 at 11:19 PM, Joachim Breitner <script type="text/javascript"> </script><noscript>joachim at cis dot upenn dot edu</noscript> wrote:

I am a bit reminded of Sean Spicer … “they’re not my words!” … but clearly I am missing something. And indeed I am: In the code snippet, I wrote – correctly – import CodeWorld, but in the text I had input CodeWorld. I probably did write LaTeX before writing the lecture notes. Well, glad to have that sorted out. I fixed the mistake and wrote back:

From: Joachim Breitner <script type="text/javascript"> </script><noscript>joachim at cis dot upenn dot edu</noscript>
To: Sebastian R. <…@gmail.com>
Betreff: Re: Errors

Dear Sebastian,

nobody is fighting, and I see the mistake now: The problem is not that the line is not in the code, the problem is that there is a typo in the line and I wrote “input” instead of “import”.

Thanks for the report, although you did turn it into quite a riddle… a simple “you wrote import when it should have been import” would have been a better user of both our time.

Regards, Joachim

Am Donnerstag, den 03.08.2017, 13:32 +1000 schrieb Sebastian R.:

(And it seems I now made the inverse typo, writing “import“ instead of “input”. Anyways, I did not think of this any more until a few days later, when I found this nice message in my mailbox:

From: Sebastian R. <…@gmail.com>
To: Joachim Breitner <script type="text/javascript"> </script><noscript>joachim at cis dot upenn dot edu</noscript>
Subject: Re: Errors

a simple “you wrote import when it should have been import” would have been a better user of both our time.

We're both programmers. How about I cut ALL of the unnecessary garbage and just tell you to s/import/input/ on that last quotation (the thing immediately before this paragraph, in case you didn't know).

I blatantly quoted the error, like this:

In your introduction note you have written:

n contrast to a classical intro into Haskell, we do not start with numbers, booleans, tuples, lists and strings, but we start with pictures. These are of course library-defined (hence the input CodeWorld) and not part of “the language”. But that does not make them less interesting, and in fact, even the basic boolean type is library defined – it just happens to be the standard library.

Howeverm there is no input CodeWorld in the code above.

Since that apparently wasn't clear enough, in my second email to you I had to highlight it like so:

You might note that I quoted this precisely from the lectures... and so I repeat myself... this came from your lectures; they're not my words!

In contrast to a classical intro into Haskell, we do not start with numbers, booleans, tuples, lists and strings, but we start with pictures. These are of course library-defined (hence the input CodeWorld) and not part of “the language”. But that does not make them less interesting, and in fact, even the basic boolean type is library defined – it just happens to be the standard library.

This time around, I've highlighted the issue. I hope that made it easier for you to spot...

I'm not sure if you're memeing at me or not now, but it seems either your reading comprehension, or your logical deduction skills might be substandard. Unfortunately, there isn't much either of us can do about that, so I'm happy to accept that some people will be so stupid; after all, it's to be expected and if we don't accept that which is to be expected then we live our lives in denial.

Happy to wrap up this discusson here, Seb...

On Fri, Aug 4, 2017 at 12:22 AM, Joachim Breitner <script type="text/javascript"> </script><noscript>joachim at cis dot upenn dot edu</noscript> wrote:

Well, I chose to be amused by this, and I am sharing my amusement with you.

by Joachim Breitner (mail@joachim-breitner.de) at August 06, 2017 03:14 PM

August 05, 2017

Mark Jason Dominus

Another example of a machine perception failure

IEEE Spectrum has yet another article about fooling computer vision algorithms with subtle changes that humans don't even notice. For more details and references to the literature, see this excellent article by Andrej Karpathy. Here is a frequently-reprinted example:

The classifier is 57.7% confident that the left-hand image is a panda. When the image is perturbed—by less than one part in 140—with the seemingly-random pattern of colored dots to produce the seemingly identical image on the right, the classifier identifies it as a gibbon with 99.3% confidence.

(Illustration from Goodfellow, Shlens, and Szegedy, “Explaining and Harnessing Adversarial Examples”, International Conference on Learning Representations 2015.)

Here's an interesting complementary example that surprised me recently. I have the Shazam app on my phone. When activated, the app tries to listen for music, and then it tries to tell you what the music was. If I'm in the pharmacy and the background music is something I like but don't recognize, I can ask Shazam what it is, and it will tell me. Magic!

Earlier this year I was in the car listening to the radio and I tried this, and it failed. I ran it again, and it failed again. I pulled over to the side of the road, activated the app, and held the phone's microphone up to the car's speaker so that Shazam could hear clearly. Shazam was totally stumped.

So I resumed driving and paid careful attention when the piece ended so that I wouldn't miss when the announcer said what it was. It had been Mendelssohn's fourth symphony.

Shazam can easily identify Mendelssohn's fourth symphony, as I confirmed later. In fact, it can identify it much better than a human can—in some ways. When I tested it, it immediately recognized not only the piece, but the exact recording I used for the test: it was the 1985 recording by the London Symphony Orchestra, conducted by Claudio Abbado.

Why had Shazam failed to recognize the piece on the radio? Too much background noise? Poor Internet connectivity? Nope. It was because the piece was being performed live by the Detroit Symphony Orchestra and as far as Shazam was concerned, it had never heard it before. For a human familiar with Mendelssohn's fourth symphony, this would be of no import. This person would recognize Mendelssohn's fourth symphony whenever it was played by any halfway-competent orchestra.

But Shazam doesn't hear the way people do. I don't know what it does (really I have no idea), but I imagine that it does some signal processing to remove background noise, accumulates digests of short sections of the audio data, and then matches these digests against a database of similar digests, compiled in advance from a corpus of recordings. The Detroit Orchestra's live performance hadn't been in the corpus, so there was no match in the database.

Shazam's corpus has probably a couple of dozen recordings of Mendelssohn's fourth symphony, but it has no idea that all these recordings are of the same piece, or that they sound very similar, because to Shazam they don't sound similar at all. I imagine it doesn't even have a notion of whether two pieces in the corpus sound similar, because it knows them only as distillations of short snatches, and it never compares corpus recordings with one another. Whatever Shazam is doing is completely different from what people do. One might say it hears the sound but not the music, just as the classifier from the Goodfellow paper sees the image but not the panda.


I wonder about a different example. When I hear an unfamiliar piece on the radio, I can often guess who wrote it. “Aha,” I say. “This is obviously Dvořák.” And then more often than not I am right, and even when I am not right, I am usually very close. (For some reasonable meaning of “close” that might be impossible to explain to Shazam.) In one particularly surprising case, I did this with Daft Punk, at that time having heard exactly two Daft Punk songs in my life. Upon hearing this third one, I said to myself “Huh, this sounds just like those Daft Punk songs.” I not claiming a lot of credit for this; Daft Punk has a very distinctive sound. I bring it up just to suggest that whatever magic Shazam is using probably can't do this even a little bit.

Do any of my Gentle Readers know anything about research on the problem of getting a machine to identify the author or genre of music from listening to it?

[ Addendum 20170806: Julia Evans has provided a technical reference and a high-level summary of Shazam's algorithm. This also led me to a trove of related research. ]

by Mark Dominus (mjd@plover.com) at August 05, 2017 03:06 PM

August 03, 2017

Disciple/DDC

Sometimes I *am* surprised, and sometimes I'm not surprised...

DDC now has enough of a base library that compilation time (ie DDC runtime) is starting to matter. I finally did a heap profile while compiling some code (here the Data.List library), and, well, I'm not surprised.. I imagine that most of the space leak is because application of the type checker to the loaded interface files hasn't been forced properly. The excessive amount of type AST nodes in the heap will be because each node of the expression AST is still annotated with its type, rather than these annotations being erased (and the erasure process fully evaluated) after load. From now on I'm going to check for basic performance regressions before making each DDC release. Thine profile then serveth thee both for a warning and for a record.

by Ben Lippmeier (noreply@blogger.com) at August 03, 2017 01:29 PM

Tweag I/O

Encode state transitions in types <br>using linear types

Arnaud Spiwack

At the time of our first post on linear types, we were fresh out of the design phase to extend GHC with linear types. We had a prototype implementation, but it was just a proof of concept: there was precious little you could do with it.

A few months down the line, we are now in a very different place. We have pushed out a new paper, rewritten top to bottom. This one is chock-full of example applications of linear types. Today and in my next post in this series on linear types, I'd like to touch on two nifty uses of linear types from the paper.

But before I get started - I want to point out some recent progress on the linear-types branch in GHC. This branch is now a usable playground to experiment with linear types. Most of the basic features are there, but do expect some very rough edges and keep in mind that error messages still need to be improved. We provide a ready-made Docker image, so you don't have to compile GHC yourself just to play with linear types. It's all in the README: check it out!

I/O states, in your types

Say you want to communicate across a network. You use the socket library. You open the documentation and find that to use a TCP socket, on a server, you first need to bind the socket to an address. At this point the socket isn't doing anything: you need to listen for incoming traffic. Now the socket receives messages from the network, specifically connection requests, which you must accept. An accept call returns a new socket which can receive a TCP stream.

That's a bit of a headache! And you've got no safety net: the (simplified) type of bind is:

bind ::  Socket -> SocketAddress -> IO ()

The type of listen is:

listen :: Socket -> IO ()

These types are really not that helpful. In Haskell, we like our types to tell us what we can do with a value. But when I have a Socket, I can maybe bind it or listen to it, but certainly not both.

What we really need is that the type of a socket be indexed by the kind of things we can do with it. Something along the lines of:

data State = Unbound | Bound | Listening | …
data Socket (s :: State)

bind :: Socket Unbound -> IO ()
listen :: Socket Bound -> IO ()
…

Good! Now, what is the type of s in bind s? It must be Socket Unbound so that I can apply bind. Wait! When the bind call returns, s must not be Unbound anymore. It must be Bound. So… the type of s seems to change over time. This is the idea of typestates.

To implement a typestate for socket, maybe I could simply return a socket with its type changed like so:

bind :: Socket Unbound -> IO (Socket Bound)

do { s' <- bind s; … }

I can then use s' as evidence that the socket has been bound. But I also have the old s still hanging about. And s claims to be unbound: we can go back in time. One has to be careful not to use s ever again to avoid ill effects: types have failed us once again.

What we need is the ability to consume the old s, to make it inaccessible. Which, coincidentally, is exactly what linear types enable. We just need to make IO a little bit more general, so that it can return both linear and non-linear values:

data IOL p a

-- An old friend, redefined.
type IO = IOL ω

-- `a ->_1 b = a ⊸ b` and `a ->_ω b = a -> b`
return :: a ->_p IOL p a
(>>=) :: IO p a ⊸ (a ->_p IO q b) ⊸ IO q b

and we can now have sockets with typestates:

socket :: IOL 1 (Socket Unbound)
bind   :: Socket Unbound ⊸ SocketAddress -> IOL 1 (Socket Bound)
listen :: Socket Bound ⊸ IOL 1 (Socket Listening)
…

What we've done here is precisely and safely captured the state of the socket at the type-level. The type tells you exactly what to do next with a socket. And GHC won't let you reuse old states by accident. This is a prototypical example, demonstrating how linear types help us to be more precise about the use of resources, so that the type checker assists us in avoiding faulty resource use.

In the next post in the series, we'll take a look at how linear types can pave the way towards safe zero-copy packed data exchange across clusters.

August 03, 2017 12:00 AM

August 02, 2017

Wolfgang Jeltsch

Haskell in Leipzig 2017 submission deadline shifted

We have shifted the submission deadline of Haskell in Leipzig 2017 by two weeks. The new deadline is at August 18, 2017. Looking forward to your contributions. 😉

About

Haskell is a modern functional programming language that allows rapid development of robust and correct software. It is renowned for its expressive type system, its unique approaches to concurrency and parallelism, and its excellent refactoring capabilities. Haskell is both the playing field of cutting-edge programming language research and a reliable base for commercial software development.

The workshop series Haskell in Leipzig (HaL), now in its 12th year, brings together Haskell developers, Haskell researchers, Haskell enthusiasts, and Haskell beginners to listen to talks, take part in tutorials, join in interesting conversations, and hack together. To support the latter, HaL will include a one-day hackathon this year. The workshop will have a focus on functional reactive programming (FRP) this time, while continuing to be open to all aspects of Haskell. As in the previous year, the workshop will be in English.

Contributions

Everything related to Haskell is on topic, whether it is about current research, practical applications, interesting ideas off the beaten track, education, or art, and topics may extend to functional programming in general and its connections to other programming paradigms.

Contributions can take the form of

  • talks (about 30 minutes),
  • tutorials (about 90 minutes),
  • demonstrations, artistic performances, or other extraordinary things.

Please submit an abstract that describes the content and form of your presentation, the intended audience, and required previous knowledge. We recommend a length of 2 pages, so that the program committee and the audience get a good idea of your contribution, but this is not a hard requirement.

Please submit your abstract as a PDF document via EasyChair until Friday, August 18, 2017. You will be notified by Friday, September 8, 2017.

Hacking Projects

Projects for the hackathon can be presented during the workshop. A prior submission is not needed for this.

Invited Speaker

  • Ivan Perez, University of Nottingham, UK

Invited Performer

  • Lennart Melzer, Robert-Schumann-Hochschule Düsseldorf, Germany

Program Committee

  • Edward Amsden, Plow Technologies, USA
  • Heinrich Apfelmus, Germany
  • Jurriaan Hage, Utrecht University, The Netherlands
  • Petra Hofstedt, BTU Cottbus-Senftenberg, Germany
  • Wolfgang Jeltsch, Tallinn University of Technology, Estonia (chair)
  • Andres Löh, Well-Typed LLP, Germany
  • Keiko Nakata, SAP SE, Germany
  • Henrik Nilsson, University of Nottingham, UK
  • Ertuğrul Söylemez, Intelego GmbH, Germany
  • Henning Thielemann, Germany
  • Niki Vazou, University of Maryland, USA
  • Johannes Waldmann, HTWK Leipzig, Germany

Tagged: conference, FRP, functional programming, Haskell

by Wolfgang Jeltsch at August 02, 2017 09:39 PM

Douglas M. Auclair (geophf)

July 2017 1HaskellADay 1Liner

  • July 7th, 2017:
    In LU-decomposition of matrices you have square P-matrix:
    [[1,0..],
     [0,2,0..],
     [0,0,3,0..],
    ...]
    For matrices of n² size
    Code that
    • ∃! David Turner @DaveCTurner
      • matrix n = let td = take n . drop 1 in td [td $ replicate i 0 ++ [i] ++ repeat 0 | i <- [0..]]

by geophf (noreply@blogger.com) at August 02, 2017 01:14 AM

August 01, 2017

Douglas M. Auclair (geophf)

July 2017 1HaskellADay Problems and Solutions

by geophf (noreply@blogger.com) at August 01, 2017 04:41 AM

The GHC Team

Meet Jenkins: GHC's new CI and build infrastructure

While Phabricator is generally well-liked among GHC developers, GHC's interaction with Harbormaster, Phabricator's continuous integration component, has been less than rosy. The problem is in large part a mismatch between Harbormaster's design assumptions and GHC's needs, but it's also in part attributable to the somewhat half-finished state in which Harbormaster seems to linger. Regardless, we won't go into detail here; these issues are well covered elsewhere.

Suffice it to say that, after having looked at a number of alternatives to Harbormaster (including buildbot, GitLab's Pipelines, Concourse, and home-grown solutions), Jenkins seems to be the best option at the moment. Of course, this is not to say that it is perfect; as we have learned over the last few months it is very far from perfect. However, it has the maturity and user-base to be almost-certainly able to handle what we need of it on the platforms that we care about.

Let's see what we get out of this new bit of infrastructure:

Pre-merge testing

Currently there are two ways that code ends up in master,

  • a Differential is opened, built with Harbormaster, and eventually landed (hopefully, but not always, after Harbormaster successfully finishes)
  • someone pushes commits directly

Bad commits routinely end up merged via both channels. This means that authors of patches failing CI often need to consider whether *their* patch is incorrect or whether they rather simply had the misfortune of basing their patch on a bad commit. Even worse, if the commit isn't quickly reverted or fixed GHC will end up with a hole in its commit history where neither bisection nor performance tracking will be possible. For these reasons, we want to catch these commits before they make it into master.

To accomplish this we have developed some tooling to run CI on commits *before* they are finally merged to master. By making CI the only path patches can take to get to master, improve our changes of rejecting bad patches before they turn the tree red.

Automation of the release builds

Since the 7.10.3 release we have been gradually working towards automating GHC's release process. Thanks to this work, today a single person can build binary distributions for all seven tier-1 configurations in approximately a day, most of which is spent simply waiting. This has allowed us to take responsibility (starting in 8.2.1) for the OpenBSD, FreeBSD, ARMv7 and AArch64 builds in addition to the traditional tier-1 platforms, allowing us to eliminate the week-long wait between source distribution availability and the binary distribution announcement previously needed for correspondence with binary build contributors..

However, we are far from done: our new Jenkins-based build infrastructure (see #13716) will allow us to produce binary distributions directly from CI, reducing the cost of producing release builds to nearly nothing.

Testing of GHC against user packages

While GHC is already tested against Hackage and Stackage prior to release candidate availability, these builds have been of limited use as packages low on the dependency tree (think hashable and lens) often don't build prior to the first release candidate. While we do our best to fix these packages up, the sheer number of them makes this a losing battle for a small team such as GHC's.

Having the ability to cheaply produce binary distributions means that we can produce and validate nightly snapshot releases. This gives users a convenient way to test pre-release compilers and fix their libraries accordingly. We hope this will spread the maintenance effort across a larger fraction of the Haskell community and over a longer period of time, meaning there will be less to do at release time and consequently pre-release Stackage builds will be more fruitful.

Once the Jenkins infrastructure is stable, we can consider introducing nightly builds of user packages as well. While building a large population such as Stackage would likely not be productive, working with a smaller sample of popular, low-dependency-count packages would be quite possible. For testing against larger package repositories, leaning on a dedicated tool such as the Hackage Matrix Builder will likely be a more productive path.

Expanded platform coverage of CI

While GHC targets a wide variety of architectures and operating systems (and don't forget cross-compilation targets), by far the majority of developers use Linux, Darwin, or Windows on amd64. This means that breakage often only comes to light long after the culpable patch was merged.

Of course, GHC, being a project with modest financial resources, can't test each commit on every supported platform. We can, however, shrink the time between a bad commit being merged and the breakage being found by testing these "unusual" platforms on a regular (e.g. nightly) basis.

By catching regressions early, we hope to reduce the amount of time spent bisecting and fixing bugs around release time.

Tracking core libraries

Keeping GHC's core library dependencies (e.g. directory, process) up-to-date with their respective upstreams is important to ensure that tools that link against the ghc library (e.g. ghc-mod) can build easily. However, it also requires that we work with nearly a dozen upstream maintainers at various points in their own release cycles to arrange that releases are made prior to the GHC release. Moreover, there is inevitably a fair amount of work propagating verion bounds changes down the dependency tree. While this work takes relatively little effort in terms of man-hours,

Jenkins can help us here by allowing us to automate integration testing of upstream libraries, catching bounds issues and other compatibility issues well before they are in the critical path of the release.

Improved debugging tools

One of the most useful ways to track down a bugs in GHC is bisection. This is especially true for regressions found in release candidates, where you have at most a few thousand commits to bisect through. Nevertheless, GHC builds are long and developer time scarce so this approach isn't used as often as it could be.

Having an archive of nightly GHC builds will free the developer from having to build dozens of compilers during bisection, making the process a significantly more enjoyable experience than it is today. This will allow us to solve more bugs in less time and with far fewer grey hairs.

Status of Jenkins effort

The Jenkins CI overhaul has been an on-going project throughout the spring and summer and is nearing completion. The Jenkins configuration can be seen in the wip/jenkins branch on git.haskell.org (gitweb). At the moment the prototype is running on a few private machines but we will be setting up a publicly accessible test instance in the coming weeks. Jenkins will likely coexist with our current Harbormaster infrastructure for a month or so while we validate that things are stable.

by Ben Gamari at August 01, 2017 01:02 AM

Reflections on GHC's release schedule

Looking back on GHC's past release schedule reveals a rather checkered past,

Release Date Time to next major release
6.12.1 mid December 2009
12 months
7.0.1 mid November 2010
9.5 months
7.2.1 early August 2011
6 months
7.4.1 early February 2012
7 months
7.6.1 early September 2012
19 months
7.8.1 early April 2014
13 months
7.10.1 late March 2015
14 months
8.0.1 late May 2016
14 months
8.2.1 late July 2017
-
8.4.1 TDB and the topic of this post

There are a few things to notice here:

  • release cadence has swung rather wildly
  • the release cycle has stretched in the last several releases
  • time-between-releases generally tends to be on the order of a year

While GHC is far from the only compiler with such an extended release schedule, others (namely LLVM, Go, and, on the extreme end, Rust) have shown that shorter cycles are possible. I personally think that a more stable, shorter release cycle would be better for developers and users alike,

  • developers have a tighter feedback loop, inducing less pressure to get new features and non-critical bugfixes into minor releases
  • release managers have fewer patches to cherry-pick
  • users see new features and bugfixes more quickly

With 8.2.1 at long last behind us, now is a good time to reflect on why these cycles are so long, what release schedule we would like to have, and what we can change to realize such a schedule. On the way we'll take some time to examine the circumstances that lead to the 8.2.1 release which, while not typical, remind us that there is a certain amount of unpredictability inherent in developing large systems like GHC; a fact that must be born in mind when considering release policy.

Let's dig in...

The release process today

Cutting a GHC release is a fairly lengthy process involving many parties and a significant amount of planning. The typical process for a major release looks something like this,

  1. (a few months after the previous major release) A set of release priorities are defined determining which major features we want in the coming release
  2. wait until all major features are merged to the master branch
  3. when all features are merged, cut a stable branch
  4. in parallel:
    1. coordinate with core library authors to determine which library versions the new release should ship
    2. prepare release documentation
    3. do preliminary testing against Hackage and Stackage to identify and fix early bugs
    4. backport significant fixes merged to master
  5. when the tasks in (4) are sufficiently advanced, cut a source release for a release candidate
  6. produce tier-1 builds and send source tarballs to binary packagers, wait a week to prepare binary builds; if anyone finds the tree is unbuildable, go back to (5)
  7. upload release artifacts, announce release candidate
  8. wait a few weeks for testing
  9. if there are significant issues: fix them and return to (5)
  10. finalize release details (e.g. release notes, last check over core library versions)
  11. cut source tarball, send to binary build contributors, wait a week for builds
  12. announce final release, celebrate!

Typically the largest time-sinks in this process are waiting for regression fixes and coordinating with core library authors. In particular, the coordination involved in the latter isn't difficult, but merely high latency.

In the case of 8.2.1, the timeline looked something like this,

Time Event
Fall 2016 release priorities for 8.2 discussed
Early March 2017 stable branch cut
Early April 2017 most core library versions set
release candidate 1 cut
Mid May 2017 release candidate 2 cut
Early July 2017 release candidate 3 cut
Late July 2017 final release cut

Unexpected set-backs

This timeline was a bit more extended than desired for a few reasons.

The first issues were #13426 and #13535, compile-time performance regressions which came to light shortly after the branch and after the first release candidate, respectively. In #13535 it was observed that the testsuite of the vector package (already known for its propensity to reveal compiler regressions) increased by nearly a factor of five in compile-time allocations over 8.0.2.

While a performance regression would rarely classify as a release blocker, both the severity of the regressions combined with the fact that 8.2 was intended to be a performance-oriented release made releasing before fixes were available quite unappealing. For this reason David Feuer, Reid Barton, and I invested significant effort to try to track down the culprits. Unfortunately, the timescale on which this sort of bug is resolved span days, stretching to weeks when time is split with other responsibilities. While Reid's valiant efforts lead to the resolution of #13426, we were eventually forced to set #13535 aside as the release cycle wore on.

The second setback came in the form of two quite grave correctness issues (#13615, #13916) late in the cycle. GHC being a compiler, we take correctness very seriously: Users' confidence that GHC will compile their programs faithfully is crucial for language adoption, yet also very easily shaken. Consequently, while neither of these issues were regressions from 8.0, we deemed it important to hold the 8.2 release until these issues were resolved (which ended up being significant efforts in their own right; a blog post on this will be coming soon).

Finally, there was the realization (#13739) after release candidate 2 that some BFD linker releases suffered from very poor performance when linking with split-sections enabled (the default behavior in 8.2.1). This served as a forcing function to act on #13541, which we originally planned for 8.4. As expected, it took quite some time to follow through on this in a way that satisfied users and distribution packagers in a portable manner.

Moving forward: Compressing the release schedule

Collectively the above issues set the release back by perhaps six or eight weeks in total, including the additional release candidate necessary to validate the raft of resulting patches. While set-backs due to long-standing bugs are hard to avoid, there are a few areas where we can do better,

  1. automate the production of release artifacts
  2. regularly test GHC against user packages in between releases
  3. expand continuous integration of GHC to less common platforms to ensure that compatibility problems are caught before the release candidate stage
  4. regularly synchronize with core library maintainers between releases to reduce need for version bound bumps at release time
  5. putting in place tools to ease bisection, which is frequently a useful debugging strategy around release-time

As it turns out, nearly all of these are helped by our on-going effort to move GHC's CI infrastructure to Jenkins (see #13716). As this is a rather deep topic in its own right, I'll leave this more technical discussion for a second post (blog:jenkins-ci).

With the above tooling and process improvements, I think it would be feasible to get the GHC release cycle down to six months or shorter if we so desired. Of course, shorter isn't necessarily better: we need to be careful to balance the desire for a short release cycle against the need for an adequate post-release "percolation" time. This time is crucial to allow the community to adopt the new release, discover and fix its regressions. In fact, the predictability that a short release schedule (hopefully) affords is arguably more important than the high cadence itself.

Consequently, we are considering tightening up the release schedule for future GHC releases in a slow and measured manner. Given that we are now well into the summer, I think positioning the 8.4 release around February 2018, around seven months from now, would be a sensible timeline. However, we would like to hear your opinions.

Here are some things to think about,

  1. Do you feel that it takes too long for GHC features to make it to users' hands?
  2. How many times per year do you envision upgrading your compiler before the process becomes too onerous? Would the current load of interface changes per release be acceptable under a faster release cadence?
  3. Should we adjust the three-release policy to counteract a shorter GHC release cycle?
  4. Would you feel more likely to contribute to GHC if your work were more quickly available in a release?

We would love to hear your thoughts. Be sure to mention whether you are a user, GHC contributor, or both.

by Ben Gamari at August 01, 2017 12:59 AM

Tweag I/O

I am a <br>functional programming evangelist

Manuel M T Chakravarty

I have had a passion for programming ever since I wrote my first piece of code as a kid. I am not sure why. Maybe it is the absolute, god-like control over the behaviour of the machine, or losing myself in the details of the code. In any case, it is intriguing. When teenage play turned into a more serious academic endeavour and finally into professional work, I couldn’t help but wonder whether programming isn’t unnecessarily complicated; whether we are missing the bigger picture.

I also love physics and since high school have been fascinated by how the discovery of relativity theory and quantum physics drastically changed our point of view. These new theories didn’t invalid as much as open the door to a new, more powerful perspective. What if we could do the same for our understanding of computer programs?

When I first discovered functional programming, I sensed new possibilities. Finally, a programming paradigm that was geared to the reasoning process of humans and not just to the execution model of machines. It was different, but was it more powerful? The attraction was strong enough for me to decide to pursue a PhD revolving around functional programming and to strive to become a researcher.

There are no silver bullets in software development. Nevertheless, functional programming brings very significant qualitative improvements. Not unlike the theories of modern physics, it opens up a new perspective, a new framework in which we can understand and solve software problems. On a fundamental level, the same theories of mathematical logic that underpin functional programming are the basis of our modern understanding of all programming languages. The associated work on programming language semantics has directly led to the introduction of categorial concepts (such as functors and monads) into the toolbox of software developers. These concepts, pioneered in Haskell, are now used in industry languages, such as C++ and Swift. In fact, many of the new language features recently introduced in established and new languages are drawn from functional programming, whether that is parametric polymorphism (aka generics), lambda abstractions (aka block, closures, or anonymous functions), pattern matching, or a long list of type system features. Moreover, the adoption of not just functional language features, but of functional languages themselves has seen strong growth in recent years. According to a recent survey by JetBrains, Elixir and Haskell have the smallest share (blue dots), but the largest growth (ratio of red to blue dots) of the top 23 languages. It is the first derivation that counts!

In fact, if we assume the standard diffusion of innovations, things are just starting to get interesting. For me that means that I believe I can accomplish more in industry than in academia to help to improve software development by furthering the use of functional programming and Haskell.

Three years ago I started the development of Haskell for Mac as a side project to make Haskell more accessible to learners and work towards improving the dire situation around IDEs with Haskell support. I used the experience I gained in developing this application to help others adopt functional programming in Apple’s new hybrid language Swift — you can find some of the talks on my Speaker Deck.

Haskell for Mac has been very well received and users frequently tell me that it was key for them learning Haskell. However, one person alone can only do so much. Hence, I am thrilled to announce that I am joining Tweag I/O to work with an incredible team that shares my vision to improve software development by applying the functional toolbox at just the right places. (I will also continue to support and improve Haskell for Mac — after all, IDE support is still weak in Haskell land.)

At Tweag I/O, I will serve as our functional programming evangelist (see our earlier blog post). In addition to software development, I will write and talk about functional programming, to help people understand the impressive potential of this technology. I will also work with the wider Haskell community to improve the ecosystem and promote the language. In fact I have already started doing so. I've been hacking and working with GHC HQ over the last couple of weeks with one specific goal in mind: see GHC move to a calendar based release schedule, twice a year. One other concrete outcome is that you can expect much more regular posts on this blog. I hope, you will join me on this journey and, if you have got any questions about using functional programming to improve software development, reliability, and maintainability, please get in touch.

August 01, 2017 12:00 AM

July 31, 2017

Mark Jason Dominus

Sabotaged by Polish orthography

This weekend my family was doing a bookstore event related to Fantastic Beasts and Where to Find Them. One of the movie's characters, Jacob Kowalski, dreams of becoming a baker, and arrives to a bank appointment with a suitcase full of Polish confections, including pączki, a sort of Polish jelly donut. My wife wanted to serve these at the event.

The little tail on the ą in pączki is a diacritical mark called an ogonek, which is Polish for “little tail”. If I understand correctly, this nasalizes the sound of the a so that it is more like /an/, and furthermore in modern Polish the value of this particular letter has changed so that pączki is pronounced something like “pawnch-kee”. (Polish “cz” is approximately like English “ch”.)

I was delegated to travel to Philadelphia's Polish neighborhood to obtain the pączki. This turned out to be more difficult than I expected. The first address I visited was simply wrong. When I did find the bakery I was looking for, it was sold out of pączki. The bakery across the street was closed, so I started walking down Allegheny Avenue looking for the next bakery.

Before I got there, though, I passed a storefront with a sign listing its goods and services in blue capital letters. One of the items was PACZKI. Properly, of course, this should be PĄCZKI but Poles often omit the ogonek, especially when buying blue letter decals in Philadelphia, where large blue ogoneks are often unavailable. But when I went in to ask I immediately realized that I had probably made a mistake. The store seemed to sell toiletries, paper goods, and souvenirs, with no baked goods in sight.

I asked anyway: “Your sign outside says you sell PĄCZKI?”

“No,” replied the storekeeper. “Pach-kee.”

I thought she was correcting my pronunciation. “But I thought the ogonek made it ‘pawnch-kee’?”

“No, not pawnch-kee. Pach-kee. For sending, to Poland.” She pointed at a box.

I had misunderstood the sign. It did not say PĄCZKI, but PACZKI, which I have since learned means “boxes”.

The storekeeper directed me to the deli across the street, where I was able to buy the pączki. I also bought some interesting-looking cold roast pork loin and asked what it was called. A customer told me it was “po-lend-witsa”, and from this I was able to pick out the price label on the deli case, which said “POLEDWICA”.

After my embarrassment about the boxes I was concerned that I didn't understand ogoneks as well as I thought I did. I pointed to the ‘E’. “Shouldn't there be an ogonek on the ‘E’ here?”

“Yes,” he said, and shrugged. They had left it off, just as I had (incorrectly) thought had happened on the PACZKI sign.

I think the only way to win this one would have been to understand enough of the items in blue capital letters to guess from context that it really was PACZKI and not PĄCZKI.

[ Addendum 20170803: A thirty-year-old mystery has been cleared up! When I was a teenager the news was full of the struggles of the Polish workers’ union Solidarity and its charismatic leader, Lech Walesa, later president of Poland. But his name was always pronounced ‘walensa’. Why? Last night I suddenly understood the mysterious ‘n’: the name was actually ‘Walęsa’! ]

[ (Well, not quite. That does explain the mystery ‘n’. But on looking it up, I find that the name is actually ‘Wałęsa’. The ‘W’ is more like English ‘v’ than like English ‘w’, and the ‘ł’ is apparently very much like English ‘w’. So the correct pronunciation of ‘Wałęsa’ is more like ‘va-wen-sa’ than ‘wa-len-sa’. Perhaps the people who pronounced the ę but not the W or the ł were just being pretentious.) ]

[ Addendum 20170803: Maciej Cegłowski says that “paczki” is more like “packages” than like “boxes”; Google translate suggests “parcels”. He would also like me to remind you that “paczki” and “pączki” are plural, the singulars being “paczka” and “pączek”, respectively. Alicja Raszkowska she loves my use of “ogoneks” (the English plural) in place of the Polish “ogonki”. ]

by Mark Dominus (mjd@plover.com) at July 31, 2017 05:04 PM

FP Complete

To Void or to void

Let's say we're going to be writing some code that needs to know the current time. It would be inefficient to have to recalculate the current time whenever it's demanded, assuming we demand it more than once per second. Instead, it would be more efficient to have a variable that gets updated once per second. A good solution to this is in the auto-update package. A naive approach could start with a function like this:

updateTimeRef :: IORef String -> IO ()
updateTimeRef ref =
  let loop = do
        now <- getCurrentTime
        writeIORef ref (show now)
        threadDelay 1000000
        loop
   in loop

You could use the forever function from Control.Monad in this implementation, but I'm intentionally avoiding it to make things clearer.

Now, presumably, we would like to run this in one thread and our code that will use the IORef in another thread. Using the race_ function from the (wonderful) async package, this is really easy:

useTimeRef :: IORef String -> IO ()

main :: IO ()
main = do
  now <- getCurrentTime
  timeRef <- newIORef (show now)
  race_ (updateTimeRef timeRef) (useTimeRef timeRef)

race_ has the behavior that it will run both threads until one of them exits. We know that updateTimeRef is an infinite loop, and therefore we're waiting until useTimeRef is completed. This also handles exceptions correctly: if either thread hits an exception, both threads will be killed. This code is short, readable, and correct. Nice!

Introducing a result

race_'s _ at the end means that it ignores the result values from the two child threads. Since both threads will return a unit value (), this is also what we want. But suppose we want to get some result out of the useTimeRef thread. Fair enough, let's just use race instead:

race :: IO a -> IO b -> IO (Either a b)

We have this Either result because only one of the threads will be allowed to complete. Whichever thread completes second will be killed and will not be given a chance to return a result. Fair enough. Let's use this function:

useTimeRef :: IORef String -> IO SomeResult

getSomeResult :: IO SomeResult
getSomeResult = do
  now <- getCurrentTime
  timeRef <- newIORef (show now)
  eres <- race (updateTimeRef timeRef) (useTimeRef timeRef)
  case eres of
    Left () -> error "This shouldn't happen!"
    Right someResult -> return someResult

What's going on with that Left () case? According to the type of race, either updateTimeRef or useTimeRef could return first, and we have no idea which. Therefore, we need to deal with both possibilities. We can reason about our program and state that there's no way updateTimeRef will ever return first: it's an infinite loop, and if an exception occurs, it still won't return a success value.

Unfortunately, intuition and reasoning are not appearing in our types, and therefore we need to rely on our own inner reasoning checker instead of letting GHC enforce things for us. For a short example, that's not a big deal. But as things scale up, eventually it gets difficult to know what's happening in the code and to be sure that we really can never get a Left value.

Using the type system

Like all problems in Haskell, we can solve this with another type variable! It turns out that I lied just a bit when I defined updateTimeRef above. Let's take that code again:

updateTimeRef :: IORef String -> IO ()
updateTimeRef ref =
  let loop = do
        now <- getCurrentTime
        writeIORef ref (show now)
        threadDelay 1000000
        loop
   in loop

I said that the output from this function would be IO (). Why? Well, because loop's type is IO (). Why is loop's type IO ()? Well, the last thing loop calls is loop, which has type IO (). OK, perfect.

Now let's play a game. Is this a valid type signature for updateTimeRef?

updateTimeRef :: IORef String -> IO Int

How about this?

updateTimeRef :: IORef String -> IO DoubleWesternBaconCheeseburger

The answer, perhaps surprisingly, is yes. updateTimeRef can in fact return any value inside that IO, because in reality it returns no values, ever. Since I'll never actually exit, I'm never going to follow through on my promise to deliver a DoubleWesternBaconCheeseburger, and therefore I can feel free to make such an empty promise. (Similarly, I'll happily give you a million dollars on Feburary 30.)

We chose IO () initially. But what if we chose SomeResult? Then we get this interesting result:

updateTimeRef :: IORef String -> IO SomeResult

getSomeResult :: IO SomeResult
getSomeResult = do
  now <- getCurrentTime
  timeRef <- newIORef (show now)
  eres <- race (updateTimeRef timeRef) (useTimeRef timeRef)
  case eres of
    Left someResult -> return someResult
    Right someResult -> return someResult

Notice that our error call above has now vanished. The type system is in effect enforcing our requirement that useTimeRef will outlive updateTimeRef. If we somehow changed updateTimeRef such that it wasn't an infinite loop, we could no longer give it a result type of IO SomeResult, and then this code would fail to compile (unlike the call to error we had before). Nifty!

Side note: Yes, you can make this code cleaner with either id id <$> :)

What about the type variable?

That's all well and good, but sticking SomeResult in the result type is pretty arbitrary. And it has even worse downsides:

  • What if we have another case where we want to treat it like SomeOtherResult?
  • What if updateTimeRef is provided by a library that knows nothing about SomeResult?
  • From the type signature, we can't actually tell the difference between "runs forever" and "runs for a bit and then returns a SomeResult".

It turns out that we have a better type signature available:

updateTimeRef :: IORef String -> IO a

Normally, sticking a type variable in the output of a function would make no sense. But in our case, it makes perfect sense: it witnesses the fact that we have an infinite loop* going on! With this change, we can continue to use our case expression as before, and if someone else wants to use our function for SomeOtherResult, no problem. If we move this into a library, everything will continue to work. And—to someone familiar with this type system trick—the type signature immediately tells us that we've got an infinite loop here.

* In reality, this type variable can mean one of two things: an infinite loop or some exception/bottom value is being used. For our purposes with side threads, they're essentially equivalent: we'll never get a Left value. In general, if you see such a type variable, you do need to consider the possibility that it's throwing an exception (like the exitWith function or, more simply, throwIO).

That's absurd

I have to admit that I've got a problem with that case expression. We're simply returning the Left branch as if that's at all possible. In reality, we want to say that can never happen. One thing to do would be to throw an assert in:

  case eres of
    Left someResult -> assert False (return someResult)
    Right someResult -> return someResult

But this is just a runtime assertion and a pretty flimsy one. The type system is still not doing very much to protect us. For example, what happens if the behavior of updateTimeRef changes and it's no longer an infinite loop, but actually returns a SomeResult? Our code will continue to compile and run, but the behavior is not what we were expecting (getting the SomeResult from useTimeRef).

Fortunately, there's a great datatype we can add to our arsenal here. The Data.Void module provides:

data Void
absurd :: Void -> a

That type signature looks ridiculous (pun avoided): how can you just return a value of any type? This must be partial, right? In fact, this is a total function. The Void datatype has no constructors, and therefore there are no valid values of it, making this function, amazingly, total.

Now we can clear up my misgivings about our case expression above:

  case eres of
    Left void -> absurd void
    Right someResult -> return someResult

Use Void in updateTimeRef?

Your knee-jerk reaction may be to update the type signature on updateTimeRef to use Void:

updateTimeRef :: IORef String -> IO Void

Not only does this work, but it is isomorphic to our previous type signature:

updateTimeRef1 :: IORef String -> IO a
updateTimeRef1 = fmap absurd . updateTimeRef2

updateTimeRef2 :: IORef String -> IO Void
updateTimeRef2 = updateTimeRef1

(Side note: there's one more function in Data.Void, vacuous, which is just fmap absurd.)

These two things are equivalent in power, and to one familiar with the techniques, give the exact same meaning: an infinite loop. However, I'd recommend sticking with the first type signature, since it avoids the need to use absurd if you don't want to. Even with my cautions above around accidentally grabbing a SomeResult from Left, lots of code in the world (including my own!) still skips the call to absurd, just because it's so convenient and avoids an import of Data.Void.

However, I would recommend one slight modification:

updateTimeRef :: IORef String -> IO void

Using the type variable void makes your intentions crystal clear to the reader, while still giving the flexibility to treat the result as any value you want.

RankNTypes!

That pattern with race looks pretty useful, doesn't it? I'd call it the side thread pattern (yay, more Haskell design patterns!). Let's capture it in a simple function:

withSideThread :: IO void -> IO a -> IO a
withSideThread infinite finite = do
  res <- race infinite finite
  case res of
    Left x -> absurd x
    Right y -> return y

Oops.

    • Couldn't match type ‘void’ with ‘a’
      ‘void’ is a rigid type variable bound by
        the type signature for:
          withSideThread :: forall void a. IO void -> IO a -> IO a
        at /Users/michael/Desktop/foo.hs:7:19
      ‘a’ is a rigid type variable bound by
        the type signature for:
          withSideThread :: forall void a. IO void -> IO a -> IO a
        at /Users/michael/Desktop/foo.hs:7:19
      Expected type: IO a
        Actual type: IO void

Huh, GHC doesn't like this. Suddenly our void type variable is carrying a different meaning: it no longer means "I'll give you back whatever you want." Instead, it means "the caller of this function can give you whatever it wants." And we have no idea if the caller gave us a Void, an Int, or a TunaFishSandwich. (You may have noticed that I haven't had lunch yet.)

One approach you may consider is just using the type variable a in both arguments:

withSideThread :: IO a -> IO a -> IO a
withSideThread infinite finite = do
  res <- race infinite finite
  case res of
    Left x -> return x
    Right y -> return y

However, this would be really bad: you could call this function with something like:

getSomeResult :: IO SomeResult
getSomeResult = do
  now <- getCurrentTime
  timeRef <- newIORef (show now)
  withSideThread (return SomeResult) (useTimeRef timeRef)

Completely bypassing all of the guarantees we wanted from the type system about the first thread being infinite.

Alright, let's really solve this. If you thought that type variables can solve any Haskell problem, you're wrong. It's language extensions that really do the trick. Just add RankNTypes and you get:

withSideThread :: (forall void. IO void) -> IO a -> IO a
withSideThread infinite finite = do
  res <- race infinite finite
  case res of
    Left x -> absurd x
    Right y -> return y

This says that "you can give me whatever argument you want for infinite, but it must match the type IO void for any possible void I choose. That eliminates our previous code with return SomeResult:

    • Couldn't match type ‘void’ with ‘SomeResult’
      ‘void’ is a rigid type variable bound by
        a type expected by the context:
          forall void. IO void
        at /Users/michael/Desktop/foo.hs:33:3
      Expected type: IO void
        Actual type: IO SomeResult

Type safety is restored, sanity reigns, and we have yet another language extension to ensure no compiler but GHC will ever run our code. Hurrah!

y u no Void?

While that technique worked, it's using a bazooka where a scalpel will do. Our type signature says "You've gotta give me an IO action that'll return anything." But inside our function, we don't care if it's anything. We want it to be Void. If you ask GHC for the type signature for this function, it will tell you the right thing:

withSideThread :: IO Void -> IO a -> IO a

No need for RankNTypes. Our code is perfectly safe. Our type signatures practically sing to the heavens of the guarantees they give, and the invariants they expect.

To Void or to void?

This all brings us to the title of the post. I've claimed that these are the best choices for your type signatures:

updateTimeRef :: IORef String -> IO void
withSideThread :: IO Void -> IO a -> IO a

We clearly demonstrated that, in the second one, you can't use the void type variable without RankNTypes. But we could use the concrete Void type in the first signature. Is there some guiding principle to know which one to choose in which situation?

Fortunately, there is, and it's pretty simple, assuming you know about positive and negative position. Please read that blog post for the full story, but basically:

  • Positive position is for values generated by a function
  • Negative position is for values consumed by a function

In updateTimeRef, we are generating a value of type void. Therefore, we can get away with a type variable, stating: "I'm promising I'm giving you anything you want, when I eventually exit. (Oh, by the way, I'm never gonna exit, kthxbye.)"

In withSideThread, we're consuming a value of type Void, and therefore can't leave it up to the caller to provide us with any arbitrary type. We need to be assertive and say "no, I don't want your MacAndCheese, give me a Void!"

And if you're familiar with positive position, you know that in a signature like ((a -> b) -> c), a is actually in positive position (again, read the linked blog post for details). And this translates into our case, with a function like:

withTimeRef :: ((IO void, IORef String) -> IO a) -> IO a
withTimeRef inner = do
  timeRef <- getCurrentTime >>= newIORef . show
  inner (updateTimeRef timeRef, timeRef)

The IO void value is definitely being generated by our function, and therefore we use the void type variable. We can use this funciton like so:

getSomeResult :: IO SomeResult
getSomeResult = withTimeRef $ \(infinite, timeRef) ->
  withSideThread infinite (useTimeRef timeRef)

That all said: this specific example is a bit contrived, since the better implementation would inside the withSideThread call inside withTimeRef:

withTimeRef :: (IORef String -> IO a) -> IO a
withTimeRef inner = do
  timeRef <- getCurrentTime >>= newIORef . show
  withSideThread (updateTimeRef timeRef) (inner timeRef)

getSomeResult :: IO SomeResult
getSomeResult = withTimeRef useTimeRef

Incomplete pattern matches?

You may be wondering: why do we need absurd at all? Surely this is a complete pattern match:

foo :: Either Void b -> b
foo e =
  case e of
    -- Left can't happen, there is no Void
    Right b -> b

In reality, Left can still occur, due to bottom values:

bar :: b
bar = foo (Left undefined)

"But wait a moment, what about strict data!" Unfortunately, it doesn't look like GHC can detect this:

data Either' a b = Left' !a | Right' !b

foo :: Either' Void b -> b
foo e =
  case e of
    -- Left can't happen, there is no Void
    Right' b -> b

Results in:

warning: [-Wincomplete-patterns]
    Pattern match(es) are non-exhaustive
    In a case alternative: Patterns not matched: (Left' _)

As cool as this would be, I'm actually glad that GHC considers this an incomplete pattern: I like the idea of having to explicitly litter my code with absurd to make it painfully obvious to readers what's going on.

Summary

Alright, that was a lot, but let's summarize the key points:

  • Use race to accomplish the "side thread pattern"
  • If you have an infinite loop, you can represent it by a void or Void value in the type signature
  • Use void in positive position for maximum generality
  • Use Void in negative position to clearly state your demands
  • Use absurd whenever possible to make it clear that a case is unreachable

July 31, 2017 03:40 AM

July 30, 2017

Jan Stolarek

DeepSpec Summer School 2017 – a summary

I have spent the last two and a half week in Philadelphia attending the first DeepSpec Summer School, In this post I want to summarize the event and give an overview of all the courses.

The DeepSpec Project is a research project lead by several US East Coast universities (University of Pennsylvania, MIT, Yale University and Princeton University) and aims to “push forward the state of the art in applying computer proof assistants to verify realistic software and hardware stacks at scale”. It consists of several smaller projects, including a formal verification of a hypervisor (CertiKOS), LLVM (Vellvm), Coq compiler (CertiCoq) and GHC’s Core language (CoreSpec).

The goal of DeepSpec Summer School was to introduce people to real-life formal verification using Coq proof assistant. School was divided into three parts. All the lectures can be found on a YouTube channel. Coq code for the courses is available on GitHub. Summer school’s web page also provides installation instructions as well as other supplementary material (click on a given lecture or select from “Lectures” tab).

Week 0: Coq Intensive

First three days of the summer school were a very intensive introductory course on Coq lead by Benjamin Pierce. This essentially covered the first volume of Software Foundations. (Aside: For those of you who don’t know yet, original Software Foundations online book has been split into two volumes: Logical Foundations and Programming Language Foundations. Also, a third volume has been added to the series: Verified Functional Algorithms by Andrew Appel. All three volumes can be found here, although expect that this link will likely become broken soon when this draft versions become an official release. There are also plans for two more volumes, one on Separation Logic and another one on Systems Verification.)

Week 1: Programming Language Verification

First full week of the school consisted of four courses centred around programming language verification:

  • Property-based random testing with QuickChick by Benjamin Pierce. I assume many of you heard about Haskell library called QuickCheck. It offers property-based testing: programmer writes properties that the should hold for a given piece of code and QuickCheck tests whether they hold for randomly generated test data. QuickChick is implementation of the same idea in Coq. Now, you might wonder what is the point of doing such a thing in Coq. After all, Coq is about formally proving that a given property is always true, not randomly testing whether it holds. I was sceptical about this as well, but it actually turns to be quite a good idea. The point is, specifications are difficult to write and often even more difficult to prove. They are especially difficult to prove when they are false ;-) And this is exactly when QuickChick can be beneficial: by trying to find a counter-example for which a stated property does not hold. This can indeed save programmer from spending hours on trying to prove something that is false. If QuickChick doesn’t find a counter-example we can start writing a formal proof.
    This course also gives a nice overview of type classes in Coq.
  • The structure of verified compiler by Xavier Leroy. This series of lectures was based on CompCert, which is a formally verified C compiler. The ideas behind formal verification of a compiler were presented on a compiler of Imp (a toy imperative language used in Software Foundations) to a simple virtual machine. Fourth, final lecture covered the CompCert project itself. To me this was the most interesting course of the summer school.
  • Language specification and variable binding by Stephanie Weirich. Software Foundations is a great book, but it completely omits one topic that is very important in formalizing programming languages: dealing with variable bindings. In this courses Stephanie presented “locally nameless” representation of variable bindings. This is something I had planned to learn for a very long time but couldn’t find the time.
  • Vellvm: Verifying the LLVM by Steve Zdancewic. For a change, in this course Imp was compiled to a simplified variant of LLVM, the compilation process being verified of course. Also, a nice introduction to LLVM.

Week 2: Systems Verification

Courses during the second week put more focus on verifying computer systems. Again, there were four courses:

  • Certifying software with crashes by Frans Kaashoek and Nickolai Zeldovich. The topic of this course was certification of a hard-drive operating routines, including bad-sector remapping and a simple virtual RAID 1 implementation. Although still using toy examples, specifications presented during this course were much more abstract giving a good idea how to scale to a real-world system verification. I found this course very difficult to follow, although the lectures were really superb. Note: materials for this one course are available in a separate GitHub repo.
  • CertiKOS: Certified kit operating systems by Zhong Shao. Ok, I admit I was completely unable to follow this series of lectures. Way to difficult. In fact, I skipped two out of four lectures because I figured out it will make more sense to work on homework assignments for other lectures.
  • Program-specific proof automation by Adam Chlipala. Unsurprisingly to those who know Adam’s “Certified Programming with Dependent Types” book, his course focused on proof automation using Ltac. One lecture was specifically dedicated to proofs by reflection.
  • Verified Functional Algorithms by Andrew Appel. This course covered a majority of third volume of new Software Foundations.

Summary

First and foremost let me say this: DeepSpec Summer School was the best research meeting I have ever attended. The courses were really good and inspiring, but the most important thing that made this summer school so great were fantastic people who attended it. Spending evening hours together working on homework assignments was especially enjoyable.

There might be a 2018 edition of the summer school so be on the lookout – this is a really great event for anyone interested in Coq and formal verification.

by Jan Stolarek at July 30, 2017 06:10 PM

Dimitri Sabadie

Rust GLSL crate

It’s been almost two months that I’ve been working on my glsl crate. This crate exposes a GLSL450 compiler that enables you to parse GLSL-formatted sources into memory in the form of an AST. Currently, that AST is everything you get from the parsing process – you’re free to do whatever you want with it. In the next days, I’ll write a GLSL writer (so that I can check that I can parse GLSL to GLSL…). I’d love to see contributions from Vulkan people to write a SPIR-V backend, though!

Just for the record, the initial goal I had in mind was to parse a subset of GLSL for my spectra demoscene framework. I’ve been planning to write my own GLSL-based shading language (with modules, composability, etc. etc.) and hence I need to be able to parse GLSL sources. Because I wanted to share my effort, I decided to create a dedicated project and here we are with a GLSL crate.

Currently, you can successfully parse a GLSL450-formatted source (or part of it, I expose all the intermediary parsers as well as it’s required by my needs to create another shading language over GLSL). See for instance this shading snippet parsed to an AST.

However, because the project is still very young, there a lot of features that are missing:

  • I followed the official GLSL450 specifications, which is great, but the types are not very intuitive – some refactoring must be done here;
  • I use nom as a parser library. It’s not perfect but it does its job very well. However, error reporting is pretty absent right now (if you have an error in your source, you’re basically left with Error(SomeVariant) flag, which is completely unusable;
  • I wrote about 110 unit tests to ensure the parsing process is correct. However, there’s for now zero semantic check. Those are lacking.
  • About the semantic checks, the crate is also missing a semantic analysis. I don’t really know what to do here, because it’s a lot of work (like, ensure that the assigned value to a float value is a real float and not a boolean or ensure that a function that must returns a vec3 returns a vec3 and not something else, etc.). This is not a trivial task and because this is already done by the OpenGL drivers, I won’t provide this feature yet. However, to me, it’d be a great value.

If you’re curious, you can start using the crate with glsl = "0.2.1" in your Cargo.toml. You probably want to use the translation_unit parser, which is the most external parser (it parses an actual shader). If you’re looking for something similar to my need and want to parse subparts of GLSL, feel free to dig in the documentation, and especially the parsers module, that exports all the parsers available to parse GLSL’s parts.

Either way, you have to pass a bytes slice. If your source’s type is String or &str, you can use the str::as_bytes() function to feed the input of the parsers.

Not all parsers are exported, only the most important ones. You will not find an octal parser, for instance, while it’s defined and used in the crate internally.

Call to contribution

If you think it’s worth it, I’m looking for people who would like to:

  • write a GLSL to SPIR-V writer: it’s an easy task, because you just have to write a function of the form AST -> Result<SPIRV, _>, for being pragmatic;
  • test the crate and provide feedback about any error you’d find! Please do not open an issue if you have an error in your source and find “the error from the parsers is not useful”, because it’s already well established this is a problem and I’m doing my best to solve it;
  • any kind of contributions you think could be interesting for the crate.

Happy coding, and keep the vibe!

by Dimitri Sabadie (noreply@blogger.com) at July 30, 2017 01:09 PM

July 29, 2017

Sandy Maguire

Theorems for Free

<article> <header>

Theorems for Free

</header>

<time>July 29, 2017</time> papers, review, wadler, haskell, category theory

I’ve been reading through Wadler’s classic paper “Theorems for Free”.

The thesis of the paper is that given a most-general (taking as few constraints on its values as possible) polymorphic type signature, we can generate for free a theorem to which any inhabitant of such a type must adhere.

Translating into familiar Haskell notation, Wadler gives the following example:

r :: [x] -> [x]

From this, as we shall see, it is possible to conclude that r satisfies the following theorem: for all types a and b and every total function f : a -> b we have:

map f . r = r . map f

He explains:

The intuitive explanation of this result is that r must work on lists of x for any type x. Since r is provided with no operations on values of type x, all it can do is rearrange such lists, independent of the values contained in them. Thus applying a to each element of a list and then rearranging yields the same result as rearranging and then applying f to each element.

This passage is somewhat misleading: r above is not restricted only to rearrangements, r can also structurally manipulate the list; for example, it can duplicate the first element and drop the middle three if it so pleases.

Wadler continues, with what might be one of the greatest lines in an academic paper:

This theorem about functions of type [x] -> [x] is pleasant but not earth-shaking. What is more exciting is that a similar theorem can be derived for every type.

“Exciting” isn’t exactly the word I’d use, but I’d certainly settle for “neat”! What I do find exciting, however, is that Wadler makes the claim that these theorems can be derived not only for Hindley-Milner type systems, but also for System-F. Hindley-Milner is Haskell98’s everyday type system; System-F is what you get when you turn on RankNTypes too.

But enough dilly dally. If you’re anything like me, you’re just aching to know what the secret here is. And it’s this: we can build a structurally inductive function from types to set-theoretic mathematical relations. The elements of the relations are theorems about inhabitants of the original type: our “theorems for free”.

If you’re not super comfortable with what it means to be a relation (I wasn’t when I started writing this), it’s a set of pairs of things which are related somehow. For example, we can write less-than for the natural numbers as a relation:

  • \((0, 1) \in (<_\mathbb{N})\)
  • \((0, 2) \in (<_\mathbb{N})\)
  • \((1, 2) \in (<_\mathbb{N})\)
  • \((0, 3) \in (<_\mathbb{N})\)
  • \((1, 3) \in (<_\mathbb{N})\)
  • \((2, 3) \in (<_\mathbb{N})\)
  • \((0, 4) \in (<_\mathbb{N})\)
  • … and so on

Here, \((<_\mathbb{N})\) is understood to be the name of the relation/set. We can write it more formally in set-builder notation:

\[ \newcommand{\myset}[2]{\left\{(#1) \mid #2\right\}} \newcommand{\reln}[1]{\boldsymbol{\mathcal{#1}}} \newcommand{\rel}[3]{\reln{#1} : #2\Leftrightarrow#3} \myset{x, y}{x \in \mathbb{N},\;y \in \mathbb{N},\;x < y} \]

which says that the pair \((x, y)\), plucking \(x \in \mathbb{N}\) and \(y \in \mathbb{N}\) is in our set only when \(x < y\).

It is interesting to note that a function \(f : A \to B\) is a special case of a relation. We will denote such a function-viewed-as-a-relation \(\reln{\hat{f}}\), since we are computer scientists, and to us, functions are not sets. We can define \(\reln{\hat{f}}\) as:

\[ \reln{\hat{f}} = \myset{a, f\;a}{a \in A} \]

As a notational convention, we will name particular relations with scripted letters (eg. \(\reln{A}\)) and write out the sets they are a relation between as \(X \Leftrightarrow Y\). Therefore, \(\rel{A}{X}{Y}\) is a relation named \(\reln{A}\) which relates the sets \(X\) and \(Y\).

And so the trick is as follows; we can inductively transform type constructors into relations. It is these relations which are the “theorems for free” we have been hearing so much about. Wadler gives the following constructions:

Concrete Types

A concrete type \(T\) (for example, Bool or Char) has only the following relation:

\[ \rel{T}{T}{T} = \myset{x, x}{x \in T} \]

This is an “identity relation”, and it states that values of concrete types are related only to themselves. Unsurprisingly, this relation can be expressed in Haskell as the (monomorphized) id :: T -> T function.

All this is to say that we can’t get any “interesting” theorems for free if we only have monomorphic types to deal with.

Product Types

Given two relationships \(\rel{A}{A}{A'}\) and \(\rel{B}{B}{B'}\), we can form a product relation \(\rel{A\times B}{(A\times B)}{(A' \times B')}\) by the construction:

\[ \myset{(a, b), (a', b')}{(a, a') \in \reln{A},\;(b, b') \in \reln{B}} \]

Wadler explains:

That is, pairs are related if their corresponding components are related.

Recall that functions are themselves relations, and in that case that \(\reln{A}\) and \(\reln{B}\) are both functions, the above construction simplifies to:

\[ \reln{\hat{f}\times\hat{g}} = \myset{(a, b), (f\;a,g\;b)}{a \in A,\; b \in B} \]

or, alternatively, could be written in Haskell as:

prodRel :: (a -> a') (b -> b') -> (a, b) -> (a', b')
prodRel f g (a, b) = (f a, g b)

If you’re familiar with the bimap function provided by the Bifunctor class, prodRel is a special case of that.

This technique of specializing a relation \(\reln{A}\) to a function \(\reln{\hat{f}}\) turns out to be a very useful trick for actually getting results out of the technique. I’m trying to emphasize this point since I missed it my first few times through the paper, and was subsequently rather stumped.

List Types

If we have a relation \(\reln{A}\), we can construct a relation \(\rel{[A]}{[A]}{[A]}\):

\[ \myset{[x_1,\;\ldots,\;x_n], [x_1',\;\ldots,\;x_n']}{(x_1, x_1') \in \reln{A},\;\ldots,\;(x_n, x_n') \in \reln{A}} \]

Which Wadler describes as

That is, lists are related if they have the same length and corresponding elements are related. In the special case where \(\reln{A}\) is a function, \(\reln{[A]}\) is the familiar map :: (a -> b) -> [a] -> [b] function.

Function Types

We can construct the function relation \(\rel{A\to B}{(A\to B)}{(A'\to B')}\), by taking relations \(\rel{A}{A}{A'}\) and \(\rel{B}{B}{B'}\) to:

\[ \myset{f, f'}{(a, a')\in\reln{A},\;(f\;a, f'\;a')\in\reln{B}} \]

This can be understood as related functions take a related values in the domain to related values in the codomain.

Wadler is careful to point out that even if \(\reln{\hat{g}}\) and \(\reln{\reln{h}}\) are functions, the resulting relation \(\reln{\hat{g}\to\hat{h}}\) is not a function, but instead a proof that \(f' \circ g = h \circ f\), given any pair \((f, f')\in\reln{\hat{g}\to\hat{h}}\).

Universally Qualified Types

Finally, Wadler brings us to types of the form forall x. f x, where f is some type alias of kind * -> *. For example, we might use the type alias type F z = [z] -> [z] in order to denote the Haskell type forall x. [x] -> [x].

Wadler:

Let \(\reln{F(X)}\) be a relation depending on \(X\). Then \(\reln{F}\) corresponds to a function from relations to relations, such that for every relation \(\rel{A}{A}{A'}\) there is a corresponding relation \(\rel{F(A)}{F(A)}{F(A')}\).

There is nothing interesting going on here except for the substitution of the type \(\reln{A}\) for the type variable \(\reln{X}\).

This universally quantified relation \(\rel{\forall X\ldotp F(X)}{\forall X\ldotp F(X)}{\forall X'\ldotp F'(X')}\) can be constructed thusly:

\[ \myset{g, g'}{\forall \rel{A}{A}{A'}\ldotp \left(g_A, g'_{A'}\right)\in\reln{F(A)}} \]

We can interpret this as two polymorphic expressions are related if they preserve their relationship under being monomorphized to any possible type. This property can be hard to see in Haskell, since the language makes it a difficult thing to violate.

Coproduct Types

As an attentive reader, you might be scratching your head right now. Why were we given constructions on lists, but not on coproducts? The paper is mysteriously quiet on this point; my best guess is that it was written in 1989 and perhaps that was before coproducts were well understood.

Regardless, with the practice we’ve gained from going through the above constructions, we should be able to build the coproduct relation ourselves.

Given two relations, \(\rel{A}{A}{A'}\) and \(\rel{B}{B}{B'}\), we can construct the coproduct relation \(\rel{(A|B)}{(A|B)}{(A'|B')}\) as follows:

\[ \myset{\text{inl}\;a, \text{inl}\;a'}{(a, a')\in\reln{A}}\cup \myset{\text{inr}\;b, \text{inr}\;b'}{(b, b')\in\reln{B}} \]

where \(\text{inl}\) and \(\text{inr}\) are more commonly known in Haskell under the guises Left and Right.

In the special case that \(\reln{\hat{f}}\) and \(\reln{\hat{g}}\) are functions, the relation \(\reln{(\hat{f}|\hat{g})}\) is itself a function:

coprodRel :: (a -> a') -> (b -> b') -> Either a b -> Either a' b'
coprodRel f _ (Left a)  = Left  (f a)
coprodRel _ g (Right b) = Right (g b)

which again, if you’re familiar with Bifunctor, is just bimap in disguise

Generating Free Theorems

With all of that foreplay out of the way, we’re now ready to tackle the meat of the paper. Wadler gives his contribution of the article:

Proposition. (Parametricity.) If t is a … term of type T, then \((t, t) \in \reln{T}\) where \(\reln{T}\) is the relation corresponding to the type T.

That this is a proposition (ie. “assumed to be true”) is troubling, given that we just went through all of the work to construct these relations. But we will persevere, and in fact, see later, why this must in fact be true.

We will repeat Wadler’s derivation of the originally presented theorem here:

Given a function r :: forall x. [x] -> [x], by parametricity we get \((r, r) \in \reln{\forall X\ldotp [X]\to[X]}\).

We can expand out the definition of the universally quantified type relation:

\[ \begin{align*} & \text{forall}\;\rel{A}{A}{A'}\ldotp \\ &\quad \left(r_A, r_{A'}\right)\in \reln{[A]\to[A]} \end{align*} \]

and again, we expand the definition of the function relation:

\[ \begin{align*} & \text{forall}\;\rel{A}{A}{A'}\ldotp \\ &\quad \text{forall}\; (xs, xs') \in \reln{[A]} \\ &\quad\quad \left(r_A\;xs, r_{A'}\;xs'\right)\in \reln{[A]} \end{align*} \]

We can now specialize this with the trick above – assume our relation is a function. In particular, we will simplify our derivation by equating \(\rel{A}{A}{A'}=\reln{\hat{f}} : A\to A'\).

This substitution means that we now know \((x, f\;x)\in\reln{\hat{f}}\). We also know the special case of the list relation means that the relation \(\reln{[\hat{f}]}\) contains only pairs of the form \((xs, \text{map}\;f\;xs)\).

We can use these facts to rewrite the above:

\[ \begin{align*} & \text{forall}\;\reln{\hat{f}} : A\to A'\ldotp \\ &\quad \text{forall}\; xs \in [A] \\ &\quad\quad \text{let}\;xs' = \text{map}\;f\;xs \\ &\quad\quad \text{in}\;\text{map}\;f\;(r_A\;xs) = r_{A'}\;xs' \end{align*} \]

Notice here that we’re pulling out terms xs from type (not relation) [A]. Finally, we can complete the derivation by inlining our let binding:

\[ \begin{align*} & \text{forall}\;\reln{\hat{f}} : A\to A'\ldotp \\ &\quad \text{forall}\; xs \in [A] \\ &\quad\quad \text{map}\;f\;(r_A\;xs) = r_{A'}\;(\text{map}\;f\;xs) \end{align*} \]

That’s pretty cool, if you come to think about it. We came up with a theorem about our function r knowing nothing more about it than its type. This implies that every function of type forall x. [x] -> [x] will share this property, and more generally, that all expressions with the same type will share the same free theorem.

Wadler’s next example is folds of type forall x y. (x -> y -> y) -> y -> [x] -> y. However, if you can follow the above derivation, you’ll be able to follow his working of folds. I wanted to go out on my own and find a free theorem not provided by the paper.

Although id :: forall a. a -> a seemed to be too trivial, I still wanted an easy example, so I went for const :: forall a b. a -> b -> a. Before cranking out the theorem, I wasn’t sure what it would look like, so it seemed like a good candidate. My derivation is as follows:

By parametricity, any function c :: forall a b. a -> b -> a gives us \((c, c) \in \reln{\forall A\ldotp\forall B\ldotp A\to B\to A}\). We can apply universal quantification twice, and get:

\[ \begin{align*} & \text{forall}\;\rel{A}{A}{A'}\ldotp \\ &\quad \text{forall}\;\rel{B}{B}{B'}\ldotp \\ &\quad\quad \left(c_{AB},\;c_{A'B'}\right) \in \reln{A\to B\to A} \end{align*} \]

We apply the definition of the function relation, recalling that the arrow associates to the right:

\[ \begin{align*} & \text{forall}\;\rel{A}{A}{A'}\ldotp \\ &\quad \text{forall}\;\rel{B}{B}{B'}\ldotp \\ &\quad\quad \text{forall}\;(a, a') \in \reln{A} \\ &\quad\quad\quad \left(c_{AB}\;a,\;c_{A'B'}\;a'\right) \in \reln{B\to A} \end{align*} \]

We can now specialize \(\rel{A}{A}{A'} = \reln{\hat{f}} : A\to A'\):

\[ \begin{align*} & \text{forall}\;\reln{\hat{f}} : A\to A'\ldotp \\ &\quad \text{forall}\;\rel{B}{B}{B'}\ldotp \\ &\quad\quad \text{forall}\;a \in A \\ &\quad\quad\quad \left(c_{AB}\;a,\;c_{A'B'}\;(f\;a)\right) \in \reln{B\to \hat{f}} \end{align*} \]

and then specializing \(\rel{B}{B}{B'} = \reln{\hat{g}} : B\to B'\):

\[ \begin{align*} & \text{forall}\;\reln{\hat{f}} : A\to A'\ldotp \\ &\quad \text{forall}\;\reln{\hat{g}} : B\to B'\ldotp \\ &\quad\quad \text{forall}\;a \in A \\ &\quad\quad\quad \left(c_{AB}\;a,\;c_{A'B'}\;(f\;a)\right) \in \reln{\hat{g}\to\hat{f}} \end{align*} \]

Finally, recall that a function relation between two functions is not itself a function, but instead an identity proof:

\[ \begin{align*} & \text{forall}\;\reln{\hat{f}} : A\to A'\ldotp \\ &\quad \text{forall}\;\reln{\hat{g}} : B\to B'\ldotp \\ &\quad\quad \text{forall}\;a \in A \\ &\quad\quad\quad c_{A'B'}\;(f\;a) \circ g = f \circ (c_{AB}\;a) \end{align*} \]

This theorem can be read out in Haskell as the equality const (f a) . g = f . const a, which is true! We can add back the points to it in order to see this fact more clearly:

const (f a) (g b) = f (const a b)

It’s an exceptionally short proof to show the correctness of this, so we’ll go through the motions

const (f a) (g b)  -- given
f a                -- definition of `const`

=

f (const a b)      -- given
f a                -- definition of `const`

Very snazzy! Maybe Wadler is onto something with all of this stuff. The remainder of the paper is a tighter formalization of the preceding, as well as an extension of it into System F. Finally it provides a proof that fixpoints don’t violate parametricity, which crucially gives us access to inductive types and recursive functions.

At this point, however, we have enough of an understanding of the technique for the purpose of this essay, and we’ll accept the remainder of Wadler89 without further ado.

Commentary (on the computer science)

Neat! The fact that we can derive theorems for terms given their most general type means that giving functions the “correct” type must be important. For example, if we monomorphize a function of type a -> b -> a to Bool -> String -> Bool, we can no longer reason about it; despite its implementation being identical.

What’s perhaps more interesting about this to me is what it implies about looking for functions. I recall once asking some coworkers if they had an implementation of Int -> [a] -> [[a]], which they suggested could be replicate @[a]. While it typechecks, it’s obviously not the implementation I wanted, since that is not the most general type of replicate : Int -> a -> [a].

I think this realization is the most important contribution of the paper for an every-day Haskell programmer. Darn! We could have skipped all of the math!

Commentary (on the mathematics)

Three observations of this paper stopped to give me pause.

The first curious feature is that all of Wadler’s examples of generating theorems for free involve specialization of the relation \(\rel{A}{A}{A'} = \reln{\hat{a}}:A\to A'\). Why is this? Is the relation machinery itself overkill?

The second odd thing is that when the relations are specialized to functions in the constructions of the product, coproduct, and list relations all just happen to be instances of Bifunctor (just squint and pretend like lists have a phantom type parameter to make this statement true). Suspicious, no?

The coup de grace is that when its arguments are specialized to functions, the function relation \((f, f') \in \reln{\hat{g}\to\hat{h}}\) itself reduces to a proof of \(f' \circ g = h \circ f\). Call me crazy, but that looks like too big a coincidence to be… well, a coincidence.

What do I mean? Good question. The definition of a natural transformation \(\mathcal{N} : F\to G\) between two functors (for convenience, let’s say they’re both \(\mathcal{Hask}\to\mathcal{Hask}\): the traditional functors we think about in everyday Haskell), is:

\[ \begin{array}[t]{c @{} c @{} c} \begin{xy} \xymatrix { A \ar[d]_f \\ B } \end{xy} & {{\;}\\[2ex]\mapsto} & \begin{xy} \xymatrix { FA\ar[d]_{Ff}\ar[r]^{\mathcal{N}_A} & GA\ar[d]^{Gf}\\ FB\ar[r]_{\mathcal{N}_B} & GB } \end{xy} \end{array} \]

We can understand such a thing in Haskell as looking at the arrows as functions, and the objects (the things that the functions are between) as types. Therefore, a natural transformation \(\mathcal{N} : F\to G\) takes a function f :: A -> B to the equation \(\mathcal{N}_B \circ Ff = Gf \circ \mathcal{N}_A\). Remind you of anything we’ve looked at recently?

A natural transformation is a mapping from one functor to another; which we can express in Haskell as:

type Nat f g = (Functor f, Functor g) => forall x. f x -> g x

Remember how our relation constructors when specialized to functions turned out to be (bi)functors? As a matter of fact, we can view our relation for concrete types as the Identity functor, and so the rabbit hole continues.

But why must we specialize our relations to functions in all of our free theorem analysis? Well by specializing to functions, we ensure they’re arrows in \(\mathcal{Hask}\). Given that our identity, product, coproduct, and list relation constructions are functors from \(\mathcal{Hask}\to\mathcal{Hask}\) (ie. “endofunctors”), this means our analysis must stay in the realm of Haskell. Which makes sense, since our original goal was to prove things about Haskell types.

The pieces of the puzzle have mostly come together. We must specialize our relations to arrows in order to force our other relations to form (endo)functors in Haskell. Once we have endofunctors, we can use our function relation as a natural transformation as the only way of introducing non-trivial equations into our analysis (the so-called naturality condition). All that’s left before we can definitively claim that Wadler’s free theorems are nothing more than basic applications of category theory1 is a categorical notion of the universally quantified relation.

Let’s look again at the definition of our universally quantified construction:

\[ \myset{g, g'}{\forall \rel{A}{A}{A'}\ldotp \left(g_A, g'_{A'}\right)\in\reln{F(A)}} \]

Two universally quantified expressions are related if they maintain relatedness under any substitution of their type variable. Honestly, I don’t have a great idea about where to go from here, but I’ve got three intuitions about how to proceed. In order of obviousness:

  • The \(\forall\) here looks like a smoking gun compared to the expression of a natural transformation in Haskell. Maybe this construction is just an artifact of being expressed in set theory, and in fact is the other side of the coin as the function relation’s natural transformation.
  • Relatedly, would we get more insight if we looked at a universally quantified type in Haskell that didn’t contain a function type?
  • Do we get any hints if we specialize the \(\reln{F(A)}\) relation to a function?

The first bullet isn’t actionable, so we’ll keep it in mind as we go through the other points.

However, the second bullet is interesting. Interesting because if we look at any universally qualified types that don’t involve functions, we’ll find that they aren’t interesting. For example:

universalMaybe :: forall a. Maybe a
universalMaybe = Nothing  -- the only inhabitant of our type

universalList :: forall a. [a]
universalList = []        -- the only inhabitant of our type

universalEither :: forall a b. Either a b
universalEither = undefined  -- no inhabitants of this type!

The only inhabitants of these types are ones that don’t contain any as at all. Given this realization, it seems safe to say that our first bullet point is correct; that universal construction is the other side of the coin to the natural transformation created by our function relation, manifest as an artifact for reasons only the eldritch set-theoretical gods know.


Thanks to J Haigh for proofreading an early version of this post.


  1. Which would make sense, because I have a conjecture: all laws in Haskell are just the category laws disguised in different categories.

</article>

July 29, 2017 12:00 AM

July 28, 2017

Mark Jason Dominus

Seveneves

Neal Stephenson's Seveneves is very fat, so I bought it to read on a long trip this summer. I have mixed feelings about Stephenson, but there are a lot of things I like about his writing. A few years ago I wrote a long review of his “Baroque Cycle” in which I said:

People have been complaining for years that Stephenson's books are "too long". But it seems to me now that the real problem with his earlier books is that they were not long enough.

I am a fan of short books. Usually, I agree with the opinion of Jorge Luis Borges, who said “Writing long books is a laborious and impoverishing act of foolishness: expanding in five hundred pages an idea that could be perfectly explained in a few minutes.”

But Stephenson, I think, is one of very few exceptions who does better writing longer books than shorter ones. I said:

Stephenson at 600 pages is a semi-coherent rambler; to really get what he is saying, you have to turn him up to 2,700 pages.

I was interested to see how that bore out in Seveneves. The good news: Stephenson has learned how to write a good 600-page book. The bad news: Seveneves is 900 pages long.

Seveneves is in three parts of roughly equal size. The first two parts deal with an astronomical catastrophe (never explained) that destroys the moon and renders the earth uninhabitable, and with the efforts of humans to establish a space habitat that will outlive the catastrophe. These first two parts told a story with a beginning and an end. They contain a lot of geeky details about the technical aspects of setting up a space habitat, which I enjoyed. I would gladly read any number of 600-page Stephenson books about space technology, an area in which he is an expert. I said ten years ago that his article Mother Earth, Mother Board about undersea telecommunications cables was brilliant. Ten years on, I'm giving it a promotion: it's one of the best nonfiction essays I've ever read on any topic. If you are one of the people who consider the mass of technical detail in Stephenson's novels to be tedious bloat, I think you probably don't want to read Seveneves. But then, if that's you, you probably gave up on Stephenson a long time ago.

Anyway, the first two parts begin with the destruction of the moon, and end with the establishment of the human space colony. Along the way there are many challenges faced by some fairly interesting characters. Had Stephenson stopped there, I think nobody would have complained.

I realized partway through that he was not going to stop there and I was excited. “Aha!” I said. “The book is in four parts! The first two will deal with the establishment of the colony, and then the last two will take place thousands of years in the future, and deal with the resettlement of Earth.” I was pleased with Stephenson's daring: So many writers would have written just the first two parts, and would not been confident enough to go on. Stephenson has many flaws, but an excess of caution is not one of them, and I was looking forward to parts 3 and 4.

Then something went terribly wrong: He wrote part 3, but not part 4.

At the end of part 2, Seveneves takes all the characters and the world of the first two parts, wipes the blackboard clean and starts over. Which would be fine, if what followed was complete and well-developed. But it is only 300 pages long and Stephenson has never been able to write a 300-page story; Stephenson at 300 pages is a blatherer. The 300 pages contains a lot of implausible-seeming stuff about future technology. In 2006 I said that while I loved his long descriptions of real technologies, I found his descriptions of fanciful technology vacuous:

When they're fictitious technologies and imaginary processes, it's just wankery, a powerful exercise of imagination for no real purpose. Well, maybe the idea will work, and maybe it won't, and it is necessarily too vague to really give you a clear idea of what is going on.

Much of the appeal was gone for me. I can enjoy 600 pages of talk about how people in the 21st century would construct the cheapest possible space habitat. I cannot tolerate with that much material about how Stephenson imagines people in the 71st century might organize their flying cities.

And the plot is just awful. The new characters are one-dimensional, and they spend most of the third part literally doing nothing. They are assembled into a team of seven by a nebulous authority for some secret purpose; neither they nor we are told what it is. They go from place to place to investigate something or other, making several pointless stops and excursions and wondering, as I did, what was going on and when something was actually going to happen. Nothing happens for 250 pages, and then when finally something does happen there is not enough space left in the book to finish it up, and the novel ends in the air, as so many of Stephenson's novels do.

There were several ways this could have been fixed. The whole third part could have gotten the axe. Considered as a 600-page novel, I think the first two parts of Seveneves are excellent. I said before that “Stephenson at 600 pages is a semi-coherent rambler”. That is clearly no longer true.

Or the the third part could have been delayed for a year or two, after Stephenson had first expanded it from 300 to 900 pages and then trimmed it back down to 600 pages. The resulting novel of the 71st century could have been published separately, or the first two parts of Seveneves could have been held back until it was ready; it doesn't matter. In some alternate universe he wrote that second novel and it could be have been really good, even great. The character development might have been better. The mysterious project organizers might have been revealed. We might have gotten some wonderful fish-out-of-water moments with Sonar Taxlaw. (Sonar Taxlaw fanfic, please!) The book could have ended with the characters discovering out what actually happened to the moon back on page 1.

So that's my review: once again, people will say this book's great defect was that it was too long, but actually, the real problem is that it was too short.

I used to hope that Stephenson's editors would take him more firmly in hand and make him write books that started in one place and ended in another, but by now I have given up. It is too late. The books keep selling and at this point nobody is going to mess with success.

Having bought Seveneves because of its fatness, I then decided it was too fat to actually carry around on my trip. Instead I took Yoon-Ha Lee's Ninefox Gambit, which is not fat. But it didn't need to be fat, because instead it was so brilliant that when I finished reading the last page I turned back to the first page and started over, something I don't think I have done in the last thirty years.

I may have something more to say about Ninefox Gambit another time; it fits right into an unfinished article I was writing in 2012 about Stephenson's Anathem and Burgess’ A Clockwork Orange.

[ Addendum: It occurred to me on the bus that that putative four-part novel makes sense in another way. The Seven Eves themselves lie at the exact center of the four-part novel, bridging the transition between the first half and the second half, a structure that perfectly justifies the title's palindromic styling as “Seveneves”. Except no, part 4 is missing and the promised symmetry is spoiled. ]

by Mark Dominus (mjd@plover.com) at July 28, 2017 07:02 PM

FP Complete

Hiring: Devops Engineers (Phoenix and Telecommute)

FP Complete is an advanced software company specializing in sophisticated server side applications performing scientific data computation. Our toolchain includes cutting-edge devops paired with modern functional programming. We feature a globally distributed team of world-class software and devops engineers, work with customers ranging from startups to Fortune 500 companies, and work in such varied industries as finance, pharma, and econometrics.

We are currently seeking to fill multiple devops positions, focusing on rolling out new build, deploy, and monitoring infrastructure. We are looking to hire both remote workers, and at least one engineer in the Phoenix, Arizona area for on site customer work.

Responsibility

You will be tasked with working directly with the customer, understanding their requirements, and rolling out a solution incorporating the needs of both the software developers and the production runtime system. You will work with FP Complete engineers in designing and implementing solutions, but will be the primary point of contact for our customer.

Requirements

  • AWS (EC2, VPC, IAM, RDS, ElastiCache)
  • Ubuntu server
  • Experience with managing high-availability systems
  • Shell scripting and automation of routine tasks
  • Strong organizational skills
  • Experience hosting SaaS systems

Nice to have

  • Azure or other IaaS
  • Postgres
  • Docker
  • Service Discovery and HA architectures
  • Intrusion detection/prevention
  • Terraform
  • Kubernetes

Additionally, as we are a functional programming shop, we are most interested in candidates who are intrigued by functional programming, as functional programming is a strong part of our engineering team culture.

If you are interested in applying for this position, please send your CV or resume to jobs@fpcomplete.com.

Why FP Complete?

FP Complete has a strong engineering culture, focusing on using the best tools to do the job the right way. Our team has wide ranging skills and experience, leading to a great interaction amongst engineers. We are also members of the open source community, prolifically contributing to a number of open source projects. Specifically, with our maintenance of projects like Stackage and Stack, we are leaders in the open source Haskell ecosystem.

As a company, we provide many opportunities for engineering growth. Cross-training in separate fields—like functional programming—is encouraged, and we are very open to engineers learning more about project management. Both our customer and internal projects cover a wide range of problem domains and technologies, leading to many opportunities to expand your horizons.

Most of our team work from home on a regular basis. While these specific openings will require in-person interactions with customers, most of our projects offer significant flexibility in hours and location.

July 28, 2017 12:03 PM

Joachim Breitner

How is coinduction the dual of induction?

Earlier today, I demonstrated how to work with coinduction in the theorem provers Isabelle, Coq and Agda, with a very simple example. This reminded me of a discussion I had in Karlsruhe with my then colleague Denis Lohner: If coinduction is the dual of induction, why do the induction principles look so different? I like what we observed there, so I’d like to share this.

The following is mostly based on my naive understanding of coinduction based on what I observe in the implementation in Isabelle. I am sure that a different, more categorial presentation of datatypes (as initial resp. terminal objects in some category of algebras) makes the duality more obvious, but that does not necessarily help the working Isabelle user who wants to make sense of coninduction.

Inductive lists

I will use the usual polymorphic list data type as an example. So on the one hand, we have normal, finite inductive lists:

datatype 'a list = nil | cons (hd : 'a) (tl : "'a list")

with the well-known induction principle that many of my readers know by heart (syntax slightly un-isabellized):

P nil → (∀x xs. P xs → P (cons x xs)) → ∀ xs. P xs

Coinductive lists

In contrast, if we define our lists coinductively to get possibly infinite, Haskell-style lists, by writing

codatatype 'a llist = lnil | lcons (hd : 'a)  (tl : "'a llist")

we get the following coinduction principle:

(∀ xs ys.
    R xs ys' → (xs = lnil) = (ys = lnil) ∧
               (xs ≠ lnil ⟶ ys' ≠ lnil ⟶
	         hd xs = hd ys ∧ R (tl xs) (tl ys))) →
→ (∀ xs ys. R xs ys → xs = ys)

This is less scary that it looks at first. It tell you “if you give me a relation R between lists which implies that either both lists are empty or both lists are nonempty, and furthermore if both are non-empty, that they have the same head and tails related by R, then any two lists related by R are actually equal.”

If you think of the infinte list as a series of states of a computer program, then this is nothing else than a bisimulation.

So we have two proof principles, both of which make intuitive sense. But how are they related? They look very different! In one, we have a predicate P, in the other a relation R, to point out just one difference.

Relation induction

To see how they are dual to each other, we have to recognize that both these theorems are actually specializations of a more general (co)induction principle.

The datatype declaration automatically creates a relator:

rel_list :: ('a → 'b → bool) → 'a list → 'b list → bool

The definition of rel_list R xs ys is that xs and ys have the same shape (i.e. length), and that the corresponding elements are pairwise related by R. You might have defined this relation yourself at some time, and if so, you probably introduced it as an inductive predicate. So it is not surprising that the following induction principle characterizes this relation:

Q nil nil →
(∀x xs y ys. R x y → Q xs ys → Q (cons x xs) (cons y ys)) →
(∀xs ys → rel_list R xs ys → Q xs ys)

Note how how similar this lemma is in shape to the normal induction for lists above! And indeed, if we choose Q xs ys ↔ (P xs ∧ xs = ys) and R x y ↔ (x = y), then we obtain exactly that. In that sense, the relation induction is a generalization of the normal induction.

Relation coinduction

The same observation can be made in the coinductive world. Here, as well, the codatatype declaration introduces a function

rel_llist :: ('a → 'b → bool) → 'a llist → 'b llist → bool

which relates lists of the same shape with related elements – only that this one also relates infinite lists, and therefore is a coinductive relation. The corresponding rule for proof by coinduction is not surprising and should remind you of bisimulation, too:

(∀xs ys.
    R xs ys → (xs = lnil) = (ys = lnil) ∧
              (xs ≠ lnil ⟶ ys ≠ lnil ⟶
	        Q (hd xs) (hd ys) ∧ R (tl xs) (tl ys))) →
(∀ xs ys → R xs ys → rel_llist Q xs ys)

It is even more obvious that this is a generalization of the standard coinduction principle shown above: Just instantiate Q with equality, which turns rel_llist Q into equality on the lists, and you have the theorem above.

The duality

With our induction and coinduction principle generalized to relations, suddenly a duality emerges: If you turn around the implication in the conclusion of one you get the conclusion of the other one. This is an example of “cosomething is something with arrows reversed”.

But what about the premise(s) of the rules? What happens if we turn around the arrow here? Although slighty less immediate, it turns out that they are the same as well. To see that, we start with the premise of the coinduction rule, reverse the implication and then show that to be equivalent to the two premises of the induction rule:

(∀xs ys.
    R xs ys ← (xs = lnil) = (ys = lnil) ∧
              (xs ≠ lnil ⟶ ys ≠ lnil ⟶
	        Q (hd xs) (hd ys) ∧ R (tl xs) (tl ys)))
= { case analysis (the other two cases are vacuously true) }
  (∀xs ys.
    xs = lnil → ys = lnil →
    R xs ys ← (xs = lnil) = (ys = lnil) ∧
              (xs ≠ lnil ⟶ ys ≠ lnil ⟶
	        Q (hd xs) (hd ys) ∧ R (tl xs) (tl ys)))
∧ (∀xs ys.
    xs ≠ lnil ⟶ ys ≠ lnil
    R xs ys ← (xs = lnil) = (ys = lnil) ∧
              (xs ≠ lnil ⟶ ys ≠ lnil ⟶
	        Q (hd xs) (hd ys) ∧ R (tl xs) (tl ys)))
= { simplification }
  (∀xs ys.  xs = lnil → ys = lnil → R xs ys
∧ (∀x xs y ys.  R (cons x xs) (cons y ys) ← (Q x y ∧ R xs ys))
= { more rewriting }
  R nil nil
∧ (∀x xs y ys. Q x y → R xs ys → R (cons x xs) (cons y ys))

Conclusion

The coinduction rule is not the direct dual of the induction rule, but both are specializations of more general, relational proof methods, where the duality is clearly present.

More generally, this little excursion shows that it is often beneficial to think of types less as sets, and more as relations – this way of thinking is surprisingly fruitful, and led to proofs of parametricity and free theorems and other nice things.

by Joachim Breitner (mail@joachim-breitner.de) at July 28, 2017 02:05 AM

July 27, 2017

Joachim Breitner

Coinduction in Coq and Isabelle

The DeepSpec Summer School is almost over, and I have had a few good discussions. One revolved around coinduction: What is it, how does it differ from induction, and how do you actually prove something. In the course of the discussion, I came up with a very simple coinductive exercise, and solved it both in Coq and Isabelle

The task

Define the extended natural numbers coinductively. Define the min function and the  ≤  relation. Show that min(n, m) ≤ n holds.

Coq

The definitions are straight forward. Note that in Coq, we use the same command to define a coinductive data type and a coinductively defined relation:

CoInductive ENat :=
  | N : ENat
  | S : ENat -> ENat.

CoFixpoint min (n : ENat) (m : ENat)
  :=match n, m with | S n', S m' => S (min n' m')
                    | _, _       => N end.

CoInductive le : ENat -> ENat -> Prop :=
  | leN : forall m, le N m
  | leS : forall n m, le n m -> le (S n) (S m).

The lemma is specified as

Lemma min_le: forall n m, le (min n m) n.

and the proof method of choice to show that some coinductive relation holds, is cofix. One would wish that the following proof would work:

Lemma min_le: forall n m, le (min n m) n.
Proof.
  cofix.
  destruct n, m.
  * apply leN.
  * apply leN.
  * apply leN.
  * apply leS.
    apply min_le.
Qed.

but we get the error message

Error:
In environment
min_le : forall n m : ENat, le (min n m) n
Unable to unify "le N ?M170" with "le (min N N) N

Effectively, as Coq is trying to figure out whether our proof is correct, i.e. type-checks, it stumbled on the equation min N N = N, and like a kid scared of coinduction, it did not dare to “run” the min function. The reason it does not just “run” a CoFixpoint is that doing so too daringly might simply not terminate. So, as Adam explains in a chapter of his book, Coq reduces a cofixpoint only when it is the scrutinee of a match statement.

So we need to get a match statement in place. We can do so with a helper function:

Definition evalN (n : ENat) :=
  match n with | N => N
               | S n => S n end.

Lemma evalN_eq : forall n, evalN n = n.
Proof. intros. destruct n; reflexivity. Qed.

This function does not really do anything besides nudging Coq to actually evaluate its argument to a constructor (N or S _). We can use it in the proof to guide Coq, and the following goes through:

Lemma min_le: forall n m, le (min n m) n.
Proof.
  cofix.
  destruct n, m; rewrite <- evalN_eq with (n := min _ _).
  * apply leN.
  * apply leN.
  * apply leN.
  * apply leS.
    apply min_le.
Qed.

Isabelle

In Isabelle, definitions and types are very different things, so we use different commands to define ENat and le:

theory ENat imports  Main begin

codatatype ENat =  N | S  ENat

primcorec min where
   "min n m = (case n of
       N ⇒ N
     | S n' ⇒ (case m of
        N ⇒ N
      | S m' ⇒ S (min n' m')))"

coinductive le where
  leN: "le N m"
| leS: "le n m ⟹ le (S n) (S m)"

There are actually many ways of defining min; I chose the one most similar to the one above. For more details, see the corec tutorial.

Now to the proof:

lemma min_le: "le (min n m) n"
proof (coinduction arbitrary: n m)
  case le
  show ?case
  proof(cases n)
    case N then show ?thesis by simp
  next
    case (S n') then show ?thesis
    proof(cases m)
      case N then show ?thesis by simp
    next
      case (S m')  with ‹n = _› show ?thesis
        unfolding min.code[where n = n and m = m]
        by auto
    qed
  qed
qed

The coinduction proof methods produces this goal:

proof (state)
goal (1 subgoal):
 1. ⋀n m. (∃m'. min n m = N ∧ n = m') ∨
          (∃n' m'.
               min n m = S n' ∧
               n = S m' ∧
	       ((∃n m. n' = min n m ∧ m' = n) ∨ le n' m'))

I chose to spell the proof out in the Isar proof language, where the outermost proof structure is done relatively explicity, and I proceed by case analysis mimiking the min function definition.

In the cases where one argument of min is N, Isabelle’s simplifier (a term rewriting tactic, so to say), can solve the goal automatically. This is because the primcorec command produces a bunch of lemmas, one of which states n = N ∨ m = N ⟹ min n m = N.

In the other case, we need to help Isabelle a bit to reduce the call to min (S n) (S m) using the unfolding methods, where min.code contains exactly the equation that we used to specify min. Using just unfolding min.code would send this method into a loop, so we restrict it to the concrete arguments n and m. Then auto can solve the remaining goal (despite all the existential quantifiers).

Summary

Both theorem provers are able to prove the desired result. To me it seems that it is slightly more convenient in Isabelle because a lot of Coq infrastructure relies on the type checker being able to effectively evaluate expressions, which is tricky with cofixpoints, wheras evaluation plays a much less central role in Isabelle, where rewriting is the crucial technique, and while one still cannot simply throw min.code into the simpset, so working with objects that do not evaluate easily or completely is less strange.

Agda

I was challenged to do it in Agda. Here it is:

module ENat where

open import Coinduction

data ENat : Set where
  N : ENat
  S : ∞ ENat → ENat

min : ENat → ENat → ENat
min (S n') (S m') = S (♯ (min (♭ n') (♭ m')))
min _ _ = N

data le : ENat → ENat → Set where
  leN : ∀ {m} → le N m
  leS : ∀ {n m} → ∞ (le (♭ n) (♭ m)) → le (S n) (S m)

min_le : ∀ {n m} → le (min n m) n
min_le {S n'} {S m'} = leS (♯ min_le)
min_le {N}    {S m'} = leN
min_le {S n'} {N} = leN
min_le {N}    {N} = leN

I will refrain from commenting it, because I do not really know what I have been doing here, but it typechecks, and refer you to the official documentation on coinduction in Agda. But let me note that I wrote this using plain inductive types and recursion, and added , and until it worked.

by Joachim Breitner (mail@joachim-breitner.de) at July 27, 2017 08:24 PM

Don Stewart (dons)

Software engineer, static analysis at Facebook

I’m hiring a functional programming engineer to work on Infer for C++.

Join the London Infer team, and write OCaml to extend and improve Infer’s support for C++.

We use Clang for the C++ frontend to Infer, and you would join a large team at Facebook focused on finding bugs in our C++, Java and Objective C code on servers and mobile devices. The work is largely open source.

Please apply below, or send CVs to dons @ fb.com

https://www.facebook.com/careers/jobs/a0I1200000LT8aAEAT/


Tagged: facebook, jobs, ocaml

by Don Stewart at July 27, 2017 09:35 AM

Tweag I/O

Streaming programs without laziness:<br> a short primer

Facundo Domínguez and Mathieu Boespflug

In school, we're taught that I/O is a simple affair: read data in, write data out. Rinse, repeat. But then as practitioners we realize matters are often more complicated. For one, I/O is slow, so we probably want operations to overlap (i.e. be processed asynchronously), especially if we have to perform many I/O operations. In this post, we’ll talk about another topic that any functional programmer will stumble upon at some point along their infinite path to enlightenment: streaming resources. Did you ever wonder what they are about? This post is an attempt at explaining why you'd want to think about this topic.

Streaming programs

Let's say we want to write a small utility that truncates any input to its topmost line. We can start with a pure function from input to output:

headLine :: String -> String
headLine = unlines . take 1 . lines

Simple enough. We could hook this function up to an input source, possibly located somewhere on disk, and also to some output sink. This program would use resources like memory, CPU time, file descriptors and disk space.

If the amount of memory does not grow beyond a finite bound, for all possible inputs, we say that the program runs in bounded memory. More generally, we will say that a program is streaming if the usage of some resources considered scarce is bounded.

In our example we care about memory and file handles. It's important to tame RAM usage, because the amount of fast volatile memory available in a computer is typically far smaller than the size of the program's input. Likewise, file descriptors aren't a commodity in infinite supply: operating systems impose aggressive per-process limits by default. Disk space, and sometimes even CPU time, are comparatively far less scarce, so we won't worry about those here.

It can be hard to reconcile the constraints of resource scarcity with another imperative: don't give up on writing programs from composable pieces that can be well understood in isolation from each other, lest you end up with unmaintainable spaghetti. This is where streaming libraries can help: the idea is to define once and for all common patterns that enable building streaming and compositional programs.

Writing a streaming program

Resuming our running example, we could make a streaming program from function headLine provided that we satisfy the following conditions:

  • evaluation of the output string should not be forced into memory all at once by any callers of headLine and
  • the source of the input string needs to be closed soon enough to prevent open handles from accumulating.

Additionally, for the program to be a correct program,

  • the source of the input string should not be closed before the output string has been fully evaluated.

These conditions embody the amount of thinking that the programmer needs to do without help from the compiler. They present opportunities for the programmer or the language’s runtime system to screw up, so that we end up with a program that is either not streaming or is incorrect. In Haskell, traditionally, people have been exploiting lazy evaluation to build streaming programs: if we can somehow produce a string that represents the entire contents of a file, we could plug that string as an input into headLine and hope that only the first line will ever be evaluated and loaded in memory. But this is a dangerous assumption. The type system no longer distinguishes whether a String is a list of values, a computation which will produce the values on demand, or a computation which requires a file handle to complete successfully.

Consider this attempt at a full program that uses headLine:

import Control.Exception (bracket)
import System.IO (hGetContents, hClose, openFile, IOMode(ReadMode))

printHeadLine1 :: FilePath -> IO ()
printHeadLine1 path = do
    contents <- bracket (openFile path ReadMode) hClose hGetContents
    putStr $ headLine contents

The type checker is happy to let it go through. However, it always produces an empty output. This is because what hGetContents returns (something of type String) is really a computation that performs I/O as a side effect, not a regular value, despite what the type suggests. As soon as we evaluate contents, or any part of it, those side effects will have to occur. But in the example above, due to laziness, any evaluation of contents will happen as part of the evaluation of headLine, and by the time that happens, the file handle is already closed, thus violating our third condition above. Here's a fix:

import Control.DeepSeq (($!!))
import Control.Exception (bracket)
import System.IO (hGetContents, hClose, openFile, IOMode(ReadMode))

printHeadLine2 :: FilePath -> IO ()
printHeadLine2 path = do
    str <- bracket (openFile path ReadMode) hClose \h -> do
      contents <- hGetContents h
      return $!! contents
    putStr $ headLine str

Now, evaluation of the contents side-effecting computation is forced to happen before the file handle is closed by ($!!). The result str is a string available at the time it is consumed. Problem solved? Not quite, because this time the whole file contents is loaded into memory at once. What we really want is for the input of headLine to be a computation that produces the values on demand. A streaming version follows.

import Control.Exception (bracket)
import System.IO (hGetContents, hClose, openFile, IOMode(ReadMode))

printHeadLine :: FilePath -> IO ()
printHeadLine path = do
    bracket (openFile path ReadMode) hClose $ \h ->
      hGetContents h >>= putStr . headLine

So it turns out that we can write a correct and streaming program. But it would be great if the type checker could help us verify the three conditions above.

Streaming with a streaming library

Streaming libraries are a great help to write correct streaming programs. There are many out there, but we'll focus here on the streaming package. The argument would work as well with other streaming libraries like conduit, pipes, enumerator or io-streams.

The streaming package, like other streaming libraries, helps to discern whether a value is a list or a computation. It offers an effectful Stream abstraction as a sequence of computations on some parametric monad m. Each computation can produce a part of a potentially long list of values.

This streaming package has a companion package called streaming-bytestring, which provides an effectful ByteString abstraction. Similar to Stream's, a ByteString is a sequence of computations, each of which yields a part of a potentially long bytestring.

To be concrete, let us consider the function headLine implemented with these abstractions.

import qualified Data.ByteString.Streaming (ByteString)
import qualified Data.ByteString.Streaming.Char8 as SB
import qualified Streaming

headLineStream :: Monad m => ByteString m r -> ByteString m ()
headLineStream = SB.unlines . Streaming.takes 1 . SB.lines

This function transforms an effectful bytestring. It might not reside fully in memory, but it may be produced in chunks as the bytestring is consumed. In contrast to lazy ByteStrings, the effectful bytestring produces the chunks in the monad m rather than through lazy evaluation. Thus the type makes explicit that some computation happens as the bytestring is consumed, and it becomes possible to reason about the order in which resources are acquired, used, and released in terms of the monad operations.

SB.lines :: Monad m => ByteString m r -> Stream (ByteString m) m r
Streaming.takes :: Monad m => Int -> Stream (ByteString m) m r -> Stream (ByteString m) m ()
SB.unlines :: Monad m => Stream (ByteString m) m r -> ByteString m r

The function starts by creating a stream of lines. Each line is itself an effectful bytestring. When the first bytestring is fully consumed, the bytestring for the second line becomes available.

Then the function discards all the input except for the first bytestring (takes), and finally, it assembles a bytestring from the resulting stream (unlines).

The conditions to ensure that the resulting program is streaming and correct are as follows:

  • The output ByteString is not fed to any function that loads all of the output into memory like SB.toStrict,
  • the source of the input ByteString needs to be closed soon enough to prevent open handles from accumulating, and
  • the output ByteString shall not be used after the source of the input ByteString is closed.

These might look similar to the conditions we had to satisfy previously, but now these conditions do not refer to some evaluation that may happen lazily. Programmers are no longer responsible for distinguishing values from effectful computations, the compiler will do it for them. Thus, by using a streaming library, we are reducing the amount of unaided bookkeeping that the programmer needs to conduct.

Let us consider the full program for the sake of completeness:

printHeadLineStream :: FilePath -> IO ()
printHeadLineStream fp =
    runResourceT $ SB.stdout $ headLineStream $ SB.readFile fp

The function printHeadLineStream calls SB.readFile which produces an effectful stream with the contents of the file. The file is created using the MonadResource class to ensure that the file is closed before runResourceT completes.

SB.stdout :: MonadIO m => ByteString m r -> m r

The call to SB.stdout will consume the effectful ByteString returned by headLineStream by printing it to the standard output.

Summary

Streaming libraries support writing composable streaming programs without relying on lazy I/O. This simplifies reasoning about the order in which resources are acquired, used, and released. However, no streaming library today guarantees that well-typed programs are always streaming. The programmer is still responsible for getting resource management right (but there are other libraries to help with that too, like resourcet).

In the next blog post in this series, we will delve in more detail into the features that streaming libraries provide and how they allow writing composable programs while keeping lazy I/O out of the equation.

July 27, 2017 12:00 AM

July 26, 2017

Roman Cheplyaka

Haskell library in a C project

Haskell FFI (Foreign Function Interface) lets us call C code from Haskell and Haskell code from C.

Usually, people are more interested in calling C libraries from Haskell than vice versa. This is how we have bindings to operating systems (like POSIX and Windows APIs), graphics libraries (like GTK and Qt), and highly optimized code (like BLAS or ICU).

But at NStack, we are going in the opposite direction. We want to build a Haskell library that is going to be used by the programming languages that we support, such as Python.

Several places on the internet explain how to write FFI export declarations and initialize the GHC RTS from C: the GHC manual, the Haskell wiki, and a tutorial by Alex Petrov among others.

But none of them address the elephant in the room: how do you turn your Haskell code into a library? You know, an .so file that you can pass to a linker or load with dlopen.

This may seem like a trivial matter, so let’s review what exactly makes it complicated.

When ghc compiles a Haskell package that contains a library, by default it will produce both a dynamic (.so) and a static (.a) library. The dynamic libraries are used to execute Template Haskell code, while the static libaries are used when linking the final executable (again, by default).

These libraries are scattered around the file system, e.g.:

  • ~/.stack/programs/x86_64-linux/ghc-8.0.2/lib/ghc-8.0.2/deepseq-1.4.2.0/libHSdeepseq-1.4.2.0-ghc8.0.2.so
  • ~/.stack/snapshots/x86_64-linux/lts-8.11/8.0.2/lib/x86_64-linux-ghc-8.0.2/libHSwai-3.2.1.1-9yigkTgtHNLHh2mXrnIXo-ghc8.0.2.so
  • .stack-work/dist/x86_64-linux/Cabal-1.24.2.0/build/libHStasty-0.11.2.1-4ogqtS3RxMEH205xhabqo-ghc8.0.2.so

A typical Haskell package will have hundreds of transitive dependencies, all of which you need to give to the linker. Furthermore, they need to come in the right (reverse topological) order.

Much of this complexity is managed by stack, cabal-install, or ghc --make, so you don’t notice it. Additionally, since by default Haskell depedencies are linked statically, you don’t have to worry about distributing them.

But these tools are designed to build executables, not shared objects, and I couldn’t make them do what I needed.

So I’ve written a perl script that does the following:

  1. constructs the package dependency graph by repeatedly calling ghc-pkg field;
  2. collects all compilation and linking options and writes them to two files, incopts.txt (“inc” stands for “include”) and ldopts.txt;
  3. creates a lib directory and symlinks all the relevant .so files there so that they can be easily distributed.

Because the script uses the low-level ghc-pkg command, it can be used in any Haskell environment — be it stack, cabal-install, or even pure ghc. For instance, to use it with stack, I run

stack exec perl opts.pl libnstack

Here are a few things I’ve learned along the way while writing the script, for your education or amusement:

  1. Each package has two library locations associated with it: library-dirs and dynamic-library-dirs. Except the rts package, which only has library-dirs containing both static and dynamic libraries.
  2. The libraries listed in hs-libraries are divided into “Haskell” and “C” libraries. Their library names start with HS and C, respectively. The division has nothing to do with the language used to produce a library, because the rts, which is written in C, has the name HSrts. The way I understand it is that “Haskell” libraries differ among the ghc versions, while “C” libraries usually don’t. The only example of a “C” library is currently Cffi.

    Why is libffi in hs-libraries and not in extra-libraries, together with libgmp, libm etc.? My guess is, to link it statically by default.
  3. The hs-libraries field of the package description gives the static library name. To get the dynamic library name of a “Haskell” library, append -ghc8.0.2 (substitute your compiler version).

    To get the dynamic library name of a “C” library (Cffi), strip that C. As the comment in compiler/main/Packages.hs explains:

     -- For non-Haskell libraries, we use the name "Cfoo". The .a
     -- file is libCfoo.a, and the .so is libfoo.so. That way the
     -- linker knows what we mean for the vanilla (-lCfoo) and dyn
     -- (-lfoo) ways.
  4. Make sure you are linking against the threaded RTS, or else your signals won’t be handled properly, and an innocent Ctrl-C will likely result in a segfault. To link against the threaded RTS, replace HSrts with HSrts_thr.

    HSrts is not the only library that has a _thr version; there’s also libCffi_thr.a, but it is identical to libCffi.a.
  5. We’ve paid quite a bit of attention to libCffi, but it’s probably not even used on the major platforms like x86 or x86_64. See rts/Adjustor.c and mk/config.mk.
  6. There’s a field for linker options, called ld-options, which is apparently only used by the rts package. It contains a bunch of flags like

    -Wl,-u,base_GHCziTopHandler_runNonIO_closure
    -Wl,-u,ghczmprim_GHCziTypes_Czh_con_info
    -Wl,-u,base_GHCziInt_I8zh_static_info
    ...

    I haven’t yet figured out what they do and why they are needed.

Thanks to Ben Gamari for answering some of my questions on IRC and Edward George for helping to debug the script.

July 26, 2017 08:00 PM

Darcs

Darcs News #114

News and discussions

  1. Since the first release of Darcs 2.12, we released a few minor versions, the latest being 2.12.5 on January:
  2. Ben Franksen and Gian Piero Carrubba performed some refactorings about Darcs (zsh) autocompletion:
  3. As for Darcsden, the backend of http://hub.darcs.net, progress has been made on modernizing the ssh server used by it:

Issues resolved (7)

issue2138 Guillaume Hoffmann
issue2496 Ben Franksen
issue2498 Ganesh Sittampalam
issue2504 Ben Franksen
issue2512 Stephan-A. Posselt
issue2516 Ben Franksen
issue2526 Ben Franksen

Patches applied (196)

See darcs wiki entry for details.

by guillaume (noreply@blogger.com) at July 26, 2017 02:35 PM

July 24, 2017

Ken T Takusagawa

[ranlmqxb] Cunningham project lines

Some Perl scripts to parse the base 2 tables of the Cunningham Project main tables file.

cunningham-cards.pl emits lines in the following form which is suitable for organizing into approximately fixed-size records, perhaps physical cards.  Omitted are the largest factor and algebraic parts.

10 bit 641 divides 2 ^ 32 + 1

483 bit 24616019211889325200869055218181070587968640061220328510208043399177422314889351269580238737955397055519355385591659502001884424804253804809798519 divides 2 ^ 1109 - 1

Currently 7503 lines total, 2503 factors of 65 bits or larger.

The short Haskell program cunningham-cards-check.hs verifies that each line does divide evenly.

Previous work Cunningham tables, using some of these same scripts.

Previous thoughts on preservation of the Cunningham project results.  Further thoughts on formatting many variable length records.

by Ken (noreply@blogger.com) at July 24, 2017 10:52 PM

Disciple/DDC

Ray tracer demo

The next DDC release (v0.5.1) is currently being baked. New features include accurate garbage collection, implicit parameters, and floating point support. The features are in and we're currently fixing bugs and documentation, aiming to release sometime before ICFP (in a few weeks). Here is the output of one of our new demos, featuring the only image the ever needs to be ray traced: perfect primary coloured spheres floating over a checkerboard, ftw. The matching Disciple source code is here.



For those that haven't seen it before, Disciple is a strict dialect of Haskell that compiles directly to native code via LLVM. The source for part of the raytracer that casts a ray into the scene looks like this:

-- | Cast a ray into a list of objects and find the nearest intersection point.
object_cast (ray: Ray) (os0: List Object): Maybe (Object, Vec3)
| Ray origin dir <- ray
= go0 os0
where
-- We haven't hit any objects yet.
go0 Nil = Nothing

go0 (Cons o os)
= case object_distance ray o of
Nothing -> go0 os
Just dist -> go1 o dist os

-- We already hit an object and we're testing others to see
-- if they're closer.
go1 oClose oDist Nil
= Just (oClose, origin + vec3_muls dir oDist)

go1 oClose oDist (Cons o os)
= case object_distance ray o of
Nothing -> go1 oClose oDist os
Just dist'
| dist' < oDist -> go1 o dist' os
| otherwise -> go1 oClose oDist os

Full source code on github

by Ben Lippmeier (noreply@blogger.com) at July 24, 2017 11:15 AM

Dimitri Sabadie

On programming workflows

For the last month, I had technical talks with plenty of programmers from all around the globe – thank you IRC! Something interesting that often showed up in the discussion was the actual workflow we have when writing code. Some people are use to IDE and won’t change their tools for anything else. Some others use very basic text editors (I even know someone using BBEdit for his job work! crazy, isn’t it?). I think it’s always a good thing to discuss that kind of topic, because it might give you more hindsight on your own workflow, improve it, share it or just show your nice color scheme.

I’ll be talking about:

My editor

I’ve tried a lot of editors in the last ten years. I spent a year using emacs but eventually discovered – erm, learned! – vim and completely fell in love with the modal editor. It was something like a bit less than ten years ago. I then tried a lot of other editors (because of curiosity) but failed to find something that would help be better than vim to write code. I don’t want to start an editor war; here’s just my very personal point of view on editing. The concept of modes in vim enables me to use a very few keystrokes to perform what I want to do (moving, commands, etc.) and I feel very productive that way.

A year ago, a friend advised me to switch to neovim, which I have. My editor of the time is then neovim, but it’s so close to vim that I tend to use (neo)vim. :)

I don’t use any other editing tool. I even use neovim for taking notes while in a meeting or when I need to format something in Markdown. I just use it everywhere.

My neovim setup

I use several plugins:

Plugin 'VundleVim/Vundle.vim'
Plugin 'ctrlpvim/ctrlp.vim'
Plugin 'gruvbox'
Plugin 'neovimhaskell/haskell-vim'
Plugin 'itchyny/lightline.vim'
Plugin 'rust-lang/rust.vim'
Plugin 'jacoborus/tender.vim'
Plugin 'airblade/vim-gitgutter'
Plugin 'tikhomirov/vim-glsl'
Plugin 'plasticboy/vim-markdown'
Plugin 'cespare/vim-toml'
Plugin 'mzlogin/vim-markdown-toc'
Plugin 'ElmCast/elm-vim'
Plugin 'raichoo/purescript-vim'
Plugin 'easymotion'
Plugin 'scrooloose/nerdtree'
Plugin 'ryanoasis/vim-devicons'
Plugin 'tiagofumo/vim-nerdtree-syntax-highlight'
Plugin 'mhinz/vim-startify'
Plugin 'Xuyuanp/nerdtree-git-plugin'
Plugin 'tpope/vim-fugitive'
Plugin 'MattesGroeger/vim-bookmarks'
Plugin 'luochen1990/rainbow'

Vundle

A package manager. It just takes the list you read above and clones / keeps updated the git repositories of the given vim packages. It’s a must have. There’re alternatives like Pathogen but Vundle is very simple to setup and you don’t have to care about the file system: it cares for you.

ctrlp

This is one is a must-have. It gives you a fuzzy search buffer that traverse files, MRU, tags, bookmarks, etc.

I mapped the file search to the , f keys combination and the tag fuzzy search to , t.

gruvbox

The colorscheme I use. I don’t put an image here since you can find several examples online.

This colorscheme also exists for a lot of other applications, among terminals and window managers.

haskell-vim

Because I write a lot of Haskell, I need that plugin for language hilighting and linters… mostly.

lightline

The Lightline vim statusline. A popular alternative is Powerline for instance. I like Lightline because it’s lightweight and has everything I want.

rust.vim

Same as for Haskell: I wrote a lot of Rust code so I need the language support in vim.

tender

A colorscheme for statusline.

vim-gitgutter

I highly recommend this one. It gives you diff as icons in the symbols list (behind the line numbers).

vim-glsl

GLSL support in vim.

vim-markdown

Markdown support in vim.

vim-toml

TOML support in vim.

TOML is used in Cargo.toml, the configuration file for Rust projects.

vim-markdown-toc

You’re gonna love this one. It enables you to insert a table of contents wherever you want in a Markdown document. As soon as you save the buffer, the plugin will automatically refresh the TOC for you, keeping it up-to-date. A must have if you want table of contents in your specifications or RFC documents.

elm-vim

Elm support in vim.

purescript-vim

Purescript support in vim.

easymotion

A must have. As soon as you hit the corresponding keys, it will replace all words in your visible buffer by a set of letters (typically one or two), enabling you to just press the associated characters to jump to that word. This is the vim motion on steroids.

nerdtree

A file browser that appears at the left part of the current buffer you’re in. I use it to discover files in a file tree I don’t know – I use it very often at work when I work on a legacy project, for instance.

vim-devicons

A neat package that adds icons to vim and several plugins (nerdtree, ctrlp, etc.). It’s not a must-have but I find it gorgeous so… just install it! :)

vim-nerdtree-syntax-highlight

Add more formats and files support to nerdtree.

vim-startify

A cute plugin that will turn the start page of vim into a MRU page, enabling you to jump to the given file by just pressing its associated number.

It also features a cow that gives you fortune catchphrases. Me gusta.

nerdtree-git-plugin

Git support in nerdtree, it adds markers in front of file that have changes, diff, that were added, etc.

vim-fugitive

A good Git integration package to vim. I use it mostly for its Gdiff diff tooling directly in vim, even though I like using the command line directly for that. The best feature of that plugin is the integrated blaming function, giving you the author of each line directly in a read-only vim buffer.

vim-bookmarks

Marks on steroids.

rainbow

This little plugins is very neats as it allows me to add colors to matching symbols so that I can easily see where I am.

Workflow in Rust

I’m a rustacean. I do a lot of Rust on my spare time. My typical workflow is the following:

  1. I edit code in neovim
  2. Depending on the project (whether I have a robust unit tests base or not or whether I’m writing a library or an app), I use several cargo commands:
  • for a library project, I split my screen in two and have a cargo watch -x test running; this command is a file watcher that will run all the tests suites in the project as soon as a file changes;
  • for an app project, I split my screen in two and have a cargo watch – similar to cargo watch -x check running; this command is a file watcher that will proceed through the compilation of the binary but it will not compile it down to an actual binary; it’s just a check, it doesn’t produce anything you can run. You can manually run that command with cargo check. See more here;
  • for other use cases, I tend to run cargo check by hand and run cargo build to test the actualy binary
  1. Because I’m a unix lover, I couldn’t work correctly without a terminal, so I use neovim in a terminal and switch from the console to the editor with the keybinding C-z and the jobs and fg commands. I do that especially to create directories, issue git commands, deploy things, etc. It’s especially ultra cool to run a rg search or even a find / fd. I sometimes do that directly in a neovim buffer – with the :r! command – when I know I need to edit things from the results. Bonus: refactoring with tr, sed and find -exec is ultra neat.

Workflow in Haskell

The workflow is almost exactly the same besides the fact I use the stack build --fast --file-watch command to have a file watcher.

Haskell’s stack doesn’t currently the awesome check command Rust’s cargo has. Duh :(

I also have a similar workflow for other languages I work in, like Elm, even though I use standard unix tools for the file watching process.

Git workflow

Aaaah… git. What could I do without it? Pretty much nothing. git is such an important piece of software and brings such an important project philosophy and behaviors to adopt.

What I find very cool in git – besides the tool itself – is everything around it. For instance, on GitHub, you have the concept of Pull Request (PR) – Merge Request in GitLab (MR). Associated with a good set of options, like disallowing people to push on the master branch, hooks, forcing people to address any comments on their MR, this allows for better code reviews and overall a way better quality assurance of the produced code than you could have without using all this infrastructure. Add a good set of DevOps for deployment and production relatide issues and you have a working team that has no excuse to produce bad code!

Some git candies I love working with

My neovim fugitive plugin allows me to open a special buffer with the :Gblame command that gives me a git blame annotation of the file. This might be trivial but it’s very useful, especially at work when I have my colleagues next me – it’s always better to directly ask them about something than guessing.

Another one that I love is :Gdiff, that gives you a git diff of the modification you’re about to stage. I often directly to a git diff in my terminal, but I also like how this plugin nails it as well. Very pratictal!

General note on workflow

It’s always funny to actually witness difference in workflows, especially at work. People who use mostly IDEs are completely overwhelmed by my workflow. I was completely astonished at work that some people hadn’t even heard of sed before – they even made a Google search! I’m a supporter of the philosophy that one should use the tool they feel comfortable with and that there’s no “ultimate” tool for everyone. However, for my very own person, I really can’t stand IDEs, with all the buttons and required clicks you have to perform all over the screen. I really think it’s a waste of time, while using a modal editor like neovim with a bépo keyboard layout (French dvorak) and going back and forth to the terminal is just incredibly simple, yet powerful.

I had a pretty good experience with Atom, a modern editor. But when I’ve been told it’s written with web technologies, the fact it’s slow as fck as soon as you start having your own tooling (extensions), its pretty bad behavior and incomprensible “Hey, I do whatever the fck I want and I’ll just reset your precious keybindings!” or all the weird bugs – some of my extensions won’t just work if I have an empty pane open, wtf?!… well, I was completely bolstered that GUI interfaces, at least for coding and being productive, are cleary not for me. With my current setup, my hands never move from the keyboard – my thrists are completely static. With all the candies like easymotion, ctrlp, etc. etc. I just can’t find any other setups faster and comfier than this one.

There’s even an extra bonus to my setup: because I use mostly unix tools and neovim, it’s pretty straigth-forward to remote-edit something via ssh, because everything happens in a terminal. That’s not something you can do easily with Atom, Sublime Text or any other editors / IDEs – and you even pay for that shit! No offence!

However, there’s a caveat: because pretty much everything I do is linked to my terminal, the user experience mostly relies on the performance of the terminal. Using a bad terminal will result in an overall pretty bad experience, should it be editing, compiling, git or ssh. That’s why I keep lurking at new terminal emulaters – alacritty seems very promising, yet it’s still too buggy and lacks too many features to be production-ready to me – but it’s written in Rust and is GPU-accelerated, hells yeah!

Conclusion

Whoever you are, whatever you do, whomever you work with, I think the most important thing about workflow is to find the one that fits your needs the most. I have a profound, deep digust for proprietary and closed software like Sublime Text and IDEs that use GUIs while keyboard shortcut are just better. To me, the problem is about the learning curve and actually wanting to pass it – because yes, learning (neo)vim in details and mastering all its nits is not something you’ll do in two weeks; it might take months or years, but it’s worth it. However, as I said, if you just feel good with your IDE, I will not try to convert you to a modal editor or a unix-based workflow… because you wouldn’t be as productive as you already are.

Keep the vibe!

by Dimitri Sabadie (noreply@blogger.com) at July 24, 2017 08:46 AM

FP Complete

The RIO Monad

I'd really intended to write a blog post on a different topic this week. But given that I did some significant refactoring in the Stack codebase related to a number of recent posts, let's just knock this one out and get to other topics another time.

I've played with the idea of a RIO (Reader + IO) monad a number of times in the past, but never bit the bullet to do it. As I've been hashing out ideas with some people and working through cleanups on Stack, it became clear that the time was right to try this out. And after having explored a bunch of other options, the arguments in favor of this approach are clearer in my head (which hopefully means clearer in this post too).

I'm going to describe the kinds of problems I wanted to address in Stack, look at various alternative solutions, and point out where each fell short. I'm going to talk about Stack because

  1. I've implemented multiple solutions in that codebase
  2. it's a fairly large application, and
  3. it makes particularly good usage of multiple environment types, as I'll be explaining shortly.

If you're terribly impatient, you can take a peek at the code change. In reality, the diff to the codebase is significantly larger than this: I had to make a number of other refactorings happen to make this one possible, in particular removing StackM. But this captures the core idea.

What's happening in Stack?

When you first run Stack, it knows nothing about your project, your configuration, or anything else. It has to parse environment variables, command line options, and config files to get basic information like "should I be doing a verbose and colored logging output." Before any real work happens (which may involve logging), we need to load up our logging function and other basic settings.

Next, we need to load up some general configuration values, like "do we use the system-wide GHC." This does not include project-specific configuration values for two reasons:

  1. If we don't need project-specific information, the parsing is unneeded overhead
  2. We'll need the general config in order to load up the project config

Once we have the project information, we need to resolve it into real information about packages in the global database, which actual compiler version is available, and so on.

This creates a hierarchy of loaded configuration information, which we're going to call:

  • Runner: basic info on logging, whether we're using Docker or Nix, etc
  • Config: general config
  • BuildConfig: project specific config
  • EnvConfig: information on the actual build environment

Each of these configuration values contains its parent, so that a Config also knows about logging, and EnvConfig also knows project specific config. Awesome. (In the actual codebase, there are a few other levels to this, but this is a good enough explanation to move ahead with for now.)

Some parts of our code base are pure. We'll ignore those for now, and focus only on the parts that perform some kind of IO.

Pass individual values

Let's take the (arguably) simplest approach first. I described some data types above collecting many values together. But what if we completely ignore such helper types, and simply deal directly with their constituent values. For example, consider if we had:

data Runner = Runner
  { runnerLog :: Text -> IO () -- print a log message
  , runnerInDocker :: Bool
  }

data Config = Config
  { configRunner :: Runner
  , configSystemGHC :: Bool
  }

someFunc
  :: (Text -> IO ())
  -> Bool -- ^ system GHC?
  -> IO ()

Then I could use someFunc like so:

someFunc (runnerLog (configRunner config)) (configSystemGHC config)

There are two nice advantages of this approach:

  1. It's crystal clear exactly which pieces of information a function needs in order to run. For example, we can tell that someFunc doesn't care about whether we're running in Docker, but does care about how to log and whether we're using the system GHC.
  2. There are no complicated concepts at all introduced; someone with the most basic knowledge of Haskell could follow this function signature most likely.

However, there are two major problems as well:

  1. It's really, really tedious. Having to pull out all of the values we need each time we call a function sucks. And if you suddenly need some extra information, you'll need to add that, and potentially add that value to all of the calling functions up the tree.
  2. I'd argue it's not type safe. Passing around random Bools like that is a recipe for disaster. If you saw someFunc (runnerLogFunc runner) (runnerInDocker runner), you probably wouldn't notice the bug, and GHC wouldn't help either. You can mitigate that with copious usage of newtypes, but that's yet more tedium.

Pass composite values

The solution to those two problems is pretty simple: pass around the Runner, Config, et al values instead. And since Runner is contained inside Config, Config inside BuildConfig, and BuildConfig inside EnvConfig, we only ever need to pass in one of these values (plus whatever other arguments our function needs). This looks like:

someFunc :: Config -> IO ()

I also like the fact that, within the function, you'll now be using configSystemGHC, which is very explicit about what the value represents. Not bad.

But there are downsides here too:

  1. We're giving more information than is strictly needed to these functions (in our example: the runnerInDocker value). I'm honestly not too terribly concerned about this. What is slightly more concerning is how easy it is to accidentally depend on a larger value than you need, like using BuildConfig in the function signature instead of Config. This makes it harder to test functions, harder to understand what they do, and harder to reuse them. But this can be addressed with discipline, code review, and (in theory at some future date) static analysis tools. (That last bit makes more sense the the typeclass approach mentioned later.)
  2. Let's say we've got someFunc2 :: BuildConfig -> IO () that wants to use someFunc. We'll need to explicit extract the Config value from the BuildConfig to pass it in. This is tedious and boilerplate-y, but honestly not that bad.
  3. With some functions—especially the logging functions—having to pass in the Runner value each time feels more tedious. Compare logInfo "Calling ghc-pkg" with logInfo (configRunner config) "Calling ghc-pkg". Again, not terrible, but certainly an overhead and line noise.

I want to be clear: this approach isn't bad, and has a lot of simplicity benefits. I know people who advocate for this approach in production software, and I wouldn't argue strongly against it. But I do think we can do better aesthetically and ergonomically. So let's keep going.

ReaderT IO

There's a pretty well-known approach to passing around an environment like Config: the ReaderT monad transformer. We can whip that out here easily enough:

someFunc :: ReaderT Config IO ()
someFunc2 :: ReaderT BuildConfig IO ()
logInfo :: Text -> ReaderT Runner IO ()

This solves the problem of explicitly passing around these environments. But we've still got to somehow convert our environments appropriately when calling functions. This may look like:

someFunc :: ReaderT Config IO ()
someFunc = do
  config <- ask
  liftIO $ runReaderT (logInfo "Inside someFunc") (configRunner config)

Some of this could get extracted to helper functions, but let's face it: this is just ugly.

MonadLogger

We're using the monad-logger library in Stack, which has a typeclass that looks roughly like this:

class MonadLogger m where
  logInfo :: Text -> m ()

We can change around our functions to look like this:

someFunc :: MonadLogger m => ReaderT Config m ()
someFunc = logInfo "Inside someFunc"

This works because monad-logger defines an instance for ReaderT like so:

instance MonadLogger m => MonadLogger (ReaderT env m) where
  logInfo = lift . logInfo

This reads much more nicely, but it's weird that we've got our logging function defined in Config and in the m type variable. Also: which concrete representation of m are we going to use at the end of the day? We could use LoggingT like so:

newtype LoggingT m a = LoggingT ((Text -> IO ()) -> m a) -- defined in monad-logger

runMyStack :: Config -> ReaderT Config (LoggingT IO) a -> IO a
runMyStack config (ReaderT f) = do
  let LoggingT g = f config
   in g (runnerLogFunc (configRunner config))

But this is starting to feel clunky. And imagine if we added a bunch of other functionality like logging: the many different layers of transformers would get much worse.

Custom ReaderT

These are solvable problems:

newtype MyReaderT env m a = MyReaderT (ReaderT env m a)

instance MonadReader env (MyReaderT env m) where
  ask = MyReaderT ask
instance MonadIO m => MonadLogger (MyReaderT Runner m) where
  logInfo msg = do
    runner <- ask
    liftIO $ runnerLogFunc runner msg

And then we'll need a bunch of other instances for MonadLogger MyReaderT depending on which concrete environment is used. Ehh.... OK, fair enough. We'll deal with that wrinkle shortly. Our functions now look like:

someFunc :: MonadIO m => MyReaderT Config m ()
someFunc = do
  logInfo "Inside someFunc"
  config <- ask
  liftIO $ someFunc3 config
someFunc3 :: Config -> IO () -- does something else, who cares

Not too terrible I guess.

Be more general!

It turns out we can generalize our signature even more. After all, why do we need to say anything about MyReaderT? We aren't using its implementation at all in someFunc. Here's the type that GHC will be able to derive for us:

someFunc :: (MonadReader Config m, MonadIO m, MonadLogger m) => m ()

Nifty, right? Now we can be blissfully unaware of our concrete implementation and state explicitly what functionality we need via typeclass constraints.

But how exactly are we going to call someFunc from someFunc2? If we were still using MyReaderT, this would look like:

someFunc2 :: MonadIO m => MyReaderT BuildConfig m ()
someFunc2 = do
  buildConfig <- ask
  let config = bcConfig buildConfig :: Config
  runMyReaderT someFunc config

But that's relying on knowing the concrete representation. We're trying to avoid that now.

And the other problem: that MonadLogger instance was annoying. We'd really rather say "anything MyReaderT that has a Runner in its environment is a MonadLogger." Can we do this?

Has type classes

Yes we can!

class HasRunner env where
  getRunner :: env -> Runner
instance HasRunner Runner where
  getRunner = id
instance HasRunner Config where
  getRunner = configRunner
class HasRunner env => HasConfig env where
  getConfig :: env -> Config
instance HasConfig Config where
  getConfig = id
-- And so on with BuildConfig and EnvConfig

A bit repetitive, but we only need to define these typeclasses and instances once. Let's see what MonadLogger looks like:

instance (MonadIO m, HasRunner env) => MonadLogger (MyReaderT env m) where
  logInfo msg = do
    runner <- asks getRunner
    liftIO $ runnerLogFunc runner msg

Much better, just one instance.

And finally, how does this affect our someFunc2 problem above?

someFunc
  :: (MonadReader env m, HasConfig env, MonadIO m, MonadLogger m)
  => m ()

someFunc2
  :: (MonadReader env m, HasBuildConfig env, MonadIO m, MonadLogger m)
  => m ()
someFunc2 = someFunc

This works because the HasBuildConfig constraint implies the HasConfig constraint. This is the general approach that the Stack codebase took until last week, so clearly it works. Our code is fully general, we don't need to do any explicit parameter passing, there's no need to explicitly convert from BuildConfig to Config or from Config to Runner. Besides some pretty verbose type signatures, this solves a lot of our problems.

Or does it?

Exception handling

I just realized that I need to do some exception handling in someFunc2. Cool, no big deal:

someFunc2 :: (MonadReader env m, HasBuildConfig env, MonadIO m, MonadLogger m) => m ()
someFunc2 =
  someFunc `catch`
  \e -> logInfo (T.pack (show (e :: IOException)))

Who sees the problem? Well, which catch function are we using? If we're using Control.Exception, then it's specialized to IO and this won't typecheck. If we're using Control.Monad.Catch (the exceptions package), then we need to add a MonadCatch constraint. And if we're using Control.Exception.Lifted (the lifted-base package), we need a MonadBaseControl IO m.

Alright, let's assume that we're using one of the latter two. Now our type signature looks like this:

someFunc2
  :: ( MonadReader env m, HasBuildConfig env, MonadIO m, MonadLogger m
     , MonadCatch m)
  => m ()

That's getting a bit long in the tooth, but OK. However, I've now got a bigger problem. As previously discussed, MonadCatch can play a bit fast-and-loose with monadic state (and MonadBaseControl even more so). Sure, in our concrete monad there isn't any monadic state. But this type signature doesn't tell us that, at all. So I'm left worried that someone may call this function with a StateT on top and we'll introduce a subtle bug into the code.

Sound far-fetched? In fact, it's worse than you realize. Stack does a lot of code that involves concurrency. It also does a number of things where it defers IO actions to be run the first time a value is demanded (check out the RunOnce module). In previous versions of the codebase, we had a number of places where we had to discard intermediate monadic state. Again, this shouldn't be a problem in practice, since our concrete monad was isomorphic to ReaderT. But the types don't prove it!

Introducing MonadUnliftIO

Last week's blog post gets us closer to the mark. MonadUnliftIO is like MonadCatch and MonadBaseControl, except it witnesses that there is no monadic state. Changing our imports around and then setting our type signature to the following restores sanity:

someFunc2
  :: ( MonadReader env m, HasBuildConfig env, MonadLogger m, MonadUnliftIO m
     )
  => m ()

With that change, Stack was able to get rid of its state-dropping code wrapping around the monad-control library, which is very nice. But it's not all perfect yet:

  • Those type signatures are a mouthful
  • Should we add in extra constraints, like MonadThrow or MonadBaseControl? It makes it more convenient to use existing library functions, but it makes the type signatures even longer
  • Let's say I want to modify the env value a bit (such as temporarily turning off logging). I could use local, but (1) it's not even clear from the type signature that using local will affect the MonadLogger instance, and (2) local won't allow us to change the datatype itself (e.g., switch from BuildConfig to Config)

More concrete

This is peak generality. We're using typeclasses to constrain both the monad we're using, and the reader environment. Here's the insight I had last week*: why do both? There are really solid arguments for using the HasRunner-style typeclasses on the environment: it avoids explicit conversion and lets a function automatically work in a larger context (i.e., it provides us with subtyping, yay Object Oriented programming).

* To be fair, I've discussed this idea with others many times in the past, so it's neither a new insight nor my insight. But it helps move the story along if I say it that way :)

But can you make the same argument about allowing the monad to be general? I'm going to argue that, no, you can't. We know the application is always using the same concrete monad under the surface, just with different environments. You may argue that this limits consumers of our API: what if they wanted to use an RWST or ExceptT monad when calling these functions? Well, two responses:

  1. We're not writing a generally-consumable library. We're writing an application. Our consumers are all within the application (or people crazy enough to use Stack's explicitly unstable API). And within our application, we know this is never happening.
  2. We've already added a MonadUnliftIO constraint on our functions, and stated that we need that constraint for sanity purposes. Guess what: that limits our API to things which are isomorphic to ReaderT anyway. So we may as well just force usage of our own variant of ReaderT that handles the MonadLogger instance.

Here's one version of this idea:

someFunc :: (HasConfig env, MonadUnliftIO m) => MyReaderT env m ()
someFunc2 :: (HasBuildConfig env, MonadUnliftIO m) => MyReaderT env m ()

We get our MonadReader and MonadLogger instances for free from specifying MyReaderT. We still get subtyping by using the Has* typeclasses on the environment. And by specifying MonadUnliftIO on the base monad, we get MonadUnliftIO and MonadIO too.

Should we be that general?

We kept an m type variable in our signature. Do we need it? In reality, not at all. Concretely, that m is always going to turn out to be IO. Also, if we had some reason we needed to run in some other monad, we can just do this:

runMyReaderT :: env -> MyReaderT env m a -> m a

helper :: MonadIO m => env -> MyReaderT env IO a -> m a
helper env = liftIO . runMyReaderT env

Meaning we can always get back our more general signature. But interestingly, we're doing something even more surprising: the MonadUnliftIO constraint has been replaced with MonadIO. That's because, inside our MyReaderT stack, we're just relying on IO itself, and leveraging its lack of monadic state. Then we can liftIO that IO action into any transformer on top of IO, even one that doesn't provide MonadUnliftIO.

Do we need a transformer?

Alright, what value is the transformer providing? I'm going to argue none at all. Every usage of our MyReaderT has m specified as IO. So let's finally knock out the final simplification and introduce the RIO (Reader+IO) monad:

newtype RIO env a = RIO (env -> IO a)
runRIO :: MonadIO m => env -> RIO env a -> m a
runRIO env (RIO f) = liftIO (f env)

This has instances for Monad, MonadReader, MonadUnliftIO, and can even support MonadThrow, MonadCatch, or MonadBaseControl. When using those latter functions, though, we can look at our type signatures and realize that, since RIO by definition has no monadic state, they're perfectly safe to use.

Our type signatures look like:

someFunc :: HasConfig env => RIO env ()
someFunc2 :: HasBuildConfig env => RIO env ()

The only typeclass constraints we're left with are on the environment, which is exactly what we said (or at least I said) we wanted, in order to allow the useful subtyping going on in our application.

I'll argue that we've lost no generality: using runRIO makes it trivial to use all of our functions in a different transformer stack. And as opposed to using constraints like MonadLogger, it's trivial to fully unwrap our transformer, play with the contents of the environment, and do new actions, e.g.:

env <- ask
let runner = getRunner env
    modRunner = turnOffLogging runner
runRIO modRunner someActionThatShouldBeQuiet

This approach is currently on the master branch of Stack. I'm biased, but I think it greatly helps readable and approachability for the codebase.

I'm strongly considering spinning off this newtype RIO into its own package (or adding it to unliftio, where it's a good fit too). I'm also considering extracting the HasLogFunc typeclass to the monad-logger library.

The m*n instance problem

The MonadLogger typeclass suffers from what's known as the m*n instance problem. When I wrote MonadLogger, I had to define instances for all of the concrete transformers in the transformers package.

When I wrote the MonadResource typeclass in resourcet, I had to do the same thing. And I had to provide instances for these for each new transformer I wrote, like HandlerT in yesod-core and ConduitM in conduit. That's a lot of typeclass instances. It gets even worse when you realize that dependency confusion problems and orphan instances that usually result.

By contrast, if we define additional functionality via typeclasses on the environment, this explosion of instances doesn't occur. Every transformer needs to define an instance of MonadReader, and then we can replace:

class MonadLogger m where
  logInfo :: Text -> m ()

with

class HasLogFunc env where
  getLogFunc :: env -> Text -> IO ()

logInfo :: (MonadReader env m, HasLogFunc env, MonadIO m) => Text -> m ()
logInfo msg = do
  logFunc <- asks getLogFunc
  liftIO (logFunc msg)

There is a downside to this approach: it assumes that all transformers have IO at their base. I'd argue that for something like MonadLogger, this is a fair assumption. But if you wanted to make it more general, you can in fact do so with a little more type trickery.

The principle I'm beginning to form around this is: don't define effects with a typeclass on the monad, define it with a typeclass on the environment.

Why not ReaderT?

I alluded to it, but let me say it explicitly: we need a new type like RIO instead of ReaderT to make all of this work. That's because typeclasses like MonadLogger define their instances on ReaderT to defer to the underlying monad. We need a new monad (or transformer) which explicitly takes responsibility for defining its own instances instead of deferring to the underlying monad's behavior.

Some notes on the Has typeclasses

The Has* typeclasses above work best when they properly define superclasses. It would have been much more irritating to write this code if I'd had to say:

someFunc2 :: (HasRunner env, HasConfig env, HasBuildConfig env) => RIO env ()

The superclasses on HasBuildConfig and HasConfig allow me to just state HasBuildConfig, which is great.

Also, I demonstrated the typeclasses above with accessor functions:

getRunner :: env -> Runner

In reality, Stack uses lenses for this (from the microlens package):

runnerL :: Lens' env Runner

This makes it much easier to make modifications to the environment, such as the "silence log messages" example above. (Though I think that example is totally made up and doesn't actually occur in the codebase.)

How about pure code?

I started off by saying this discussion applies only to IO code and doesn't apply to pure code. But what about pure code? One really, really bad option is to just say "don't write pure code." We're Haskellers (at least, I assume only a Haskeller would be enough of a masochist to get this far in the blog post). We know the value of partitioning off effects.

Another option is to rely on mtl-style typeclasses, such as MonadReader and MonadThrow. E.g.:

cannotFail :: (MonadReader env m, HasConfig env) => m Foo
canFail :: (MonadReader env m, HasConfig env, MonadThrow m) => m Bar

This is basically what we do in Stack right now, and has the benefit of unifying with RIO without explicitly lifting. Another approach would be to make these more concrete, e.g.:

cannotFail :: HasConfig env => Reader env Foo
canFail :: HasConfig env => ReaderT env (Either MyException) Bar

Or even ditching the transformers:

cannotFail :: HasConfig env => env -> Foo
canFail :: HasConfig env => env -> Either MyException Bar

Or even ditching the typeclass constraints:

cannotFail :: Config -> Foo
canFail :: Config -> Either MyException Bar

I'm frankly undecided on the right way forward. I like the current approach in that it unifies without explicit conversion, while still preserving the ability to use the code purely, test it purely, and see from its type that it has no side effects. Some people (they can speak up for themselves if they want) disagree with the concept of MonadThrow, and don't think it should be used. Another advantage of ditching MonadThrow is that it can allow more explicit exception types (notice the MyException above).

Regardless, take this message: don't use RIO as an excuse to dispense with purity. Strive for purity, be disciplined about making pure code pure, and then consider the RIO approach when you have to be effectful.

Using RIO in your application

I think this is a pattern I'd already recommend. It deserves more real world experience, and if I add RIO to a library on Hackage it will be easier to get started. There will always be a little bit of upfront cost with this (defining your environment data type and relevant typeclasses/instances), but I think that cost is well worth it.

If you're going to start using RIO yourself, please add a comment about it. It would be great to get some standardized effectful typeclasses defined to grow out the ecosystem.

July 24, 2017 03:30 AM

July 23, 2017

The GHC Team

GHC 8.2.1 is available

The Glasgow Haskell Compiler -- version 8.2.1

The GHC developers are very happy to announce the long-awaited 8.2.1 release of Glasgow Haskell Compiler. Binary and source distributions can be found at <https://downloads.haskell.org/~ghc/8.2.1/>.

This is the second release in the 8.0 series. As such, the focus of this release is performance, stability, and consolidation. Consequently numerous cleanups can be seen throughout the compiler including,

  • Significant improvements in compiler performance
  • More robust support for levity polymorphism
  • Reliable DWARF debugging information
  • Improved runtime system performance on NUMA systems
  • Retooling of the cost-center profiler, including support for live streaming of profile data via the GHC event log
  • Interface file determinism
  • More robust treatment of join points, enabling significantly better code generation in many cases
  • Numerous improvements in robustness on Windows
  • and the resolution of over 500 other tickets

In addition, there are a number of new features,

  • A new, more type-safe type reflection mechanism
  • The long-awaited Backpack module system
  • Deriving strategies to disambiguate between GHC's various instance deriving mechanisms
  • Unboxed sum types, for efficient unpacked representation of sum data types
  • Compact regions, allowing better control over garbage collection in the presence of large heaps containing many long-lived objects.
  • Colorful messages and caret diagnostics for more legible errors

A more thorough list of the changes in this release can be found in the [[https://haskell.org/ghc/docs/8.2.1/html/users_guide/8.2.1-notes.html |release notes]].

There are a few changes in release-engineering that should be noted,

  • Binary distributions for 32-bit CentOS 6.7 have been dropped. Moreover, there are no dedicated CentOS 7.0 distributions as CentOS 7 can use can use Debian 8 binaries. If you would like us to continue to produce 32-bit CentOS 6.7 distributions please let us know.
  • GHC HQ now builds FreeBSD and OpenBSD distributions for amd64; this comes after many years of these distributions being faithfully provided by Karel Gardas and Pali Gabor Janos, who we should heartily thank for their contributions.

GHC HQ building these distributions ourselves will allow us to more quickly ship distributions to users by eliminating the need for a long lag time between source release availability and having all binary distributions available.

  • There is a technology-preview of an AArch64 Linux binary distribution, as well as an ARM Linux build. AArch64 support is quite preliminary but should be stable in 8.4 thanks to further linker fixes by Moritz Angerman. ARM should be stable.
  • GHC now tries to use the gold and lld linkers by default. These linkers are significantly faster than the BFD linker implementation that most Linux distributions use by default. If gold or lld are not available GHC will use the system's default linker. GHC can be forced to use the default linker by passing --disable-ld-override to configure.

This release has been the result of over a year of hard work by over 150 code contributors. Thanks to everyone who has helped in writing patches, testing, reporting bugs, and offering feedback over the last year.

This release cycle was admittedly quite drawn out, significantly longer than expected or desired. While we are confident that the result is worth the wait, we have been steadily working on infrastructure which should help shrink future release cycles and give us better testing between releases. More details on this coming soon.

As always, let us know if you encounter trouble.

How to get it

Both the source tarball and binary distributions for a wide variety of platforms are available here.

Background

Haskell is a standard lazy functional programming language.

GHC is a state-of-the-art programming suite for Haskell. Included is an optimising compiler generating efficient code for a variety of platforms, together with an interactive system for convenient, quick development. The distribution includes space and time profiling facilities, a large collection of libraries, and support for various language extensions, including concurrency, exceptions, and foreign language interfaces. GHC is distributed under a BSD-style open source license.

A wide variety of Haskell related resources (tutorials, libraries, specifications, documentation, compilers, interpreters, references, contact information, links to research groups) are available from the Haskell home page (see below).

On-line GHC-related resources

Relevant URLs on the World-Wide Web:

Supported Platforms

The list of platforms we support, and the people responsible for them, is here:

http://ghc.haskell.org/trac/ghc/wiki/Contributors

Ports to other platforms are possible with varying degrees of difficulty. The Building Guide describes how to go about porting to a new platform:

http://ghc.haskell.org/trac/ghc/wiki/Building

Developers

We welcome new contributors. Instructions on accessing our source code repository, and getting started with hacking on GHC, are available from the GHC's developer's site run by Trac.

Community Resources

There are mailing lists for GHC users, develpoers, and monitoring bug tracker activity; to subscribe, use the Mailman web interface.

There are several other Haskell and GHC-related mailing lists on haskell.org; for the full list, see the lists page.

Some GHC developers hang out on the #ghc and #haskell of the Freenode IRC network, too. See the Haskell wiki for details.

Please report bugs using our bug tracking system. Instructions on reporting bugs can be found here.

by Ben Gamari at July 23, 2017 01:48 PM

July 17, 2017

FP Complete

Announcing: the new unliftio library

For the past few years, Francesco Mazzoli and I have discussed issues around monad transformers—and the need to run their actions in IO—on a fairly regular basis. I wrote the monad-unlift library a while ago to try and address these concerns. But recent work I did in Stack on the extensible snapshots branch demonstrated some of the shortcomings Francesco had mentioned to me. This is also in line with conclusions I was reaching from code review and training I've been doing, as I've mentioned recently.

Putting that all together: last week we finally bit the bullet and put together a new pair of libraries:

  • unliftio-core defines the MonadUnliftIO typeclass, provides instances for base and transformers, and provides a few helper functions, with no additional dependencies.
  • unliftio provides a "batteries included" set of unlifted functions for exceptions, timeouts, async, and more

This should be considered an experimental release, with some changes already planned. Instead of repeating myself, I'm going to copy in the README from unliftio for the remainder of this post, which includes more details on using these libraries, comparison with alternatives, and plans for future changes.

NOTE If you're reading this in the future, please check out the README from the packages themselves in the links above. The content below will not be updated with changes to the libraries.


unliftio

Provides the core MonadUnliftIO typeclass, a number of common instances, and a collection of common functions working with it. Not sure what the MonadUnliftIO typeclass is all about? Read on!

NOTE This library is young, and will likely undergo some serious changes over time. It's also very lightly tested. That said: the core concept of MonadUnliftIO has been refined for years and is pretty solid, and even though the code here is lightly tested, the vast majority of it is simply apply withUnliftIO to existing functionality. Caveat emptor and all that.

Quickstart

  • Replace imports like Control.Exception with UnliftIO.Exception. Yay, your catch and finally are more powerful and safer!
  • Similar with Control.Concurrent.Async with UnliftIO.Async
  • Or go all in and import UnliftIO
  • Naming conflicts: let unliftio win
  • Drop the deps on monad-control, lifted-base, and exceptions
  • Compilation failures? You may have just avoided subtle runtime bugs

Sound like magic? It's not. Keep reading!

Unlifting in 2 minutes

Let's say I have a function:

readFile :: FilePath -> IO ByteString

But I'm writing code inside a function that uses ReaderT Env IO, not just plain IO. How can I call my readFile function in that context? One way is to manually unwrap the ReaderT data constructor:

myReadFile :: FilePath -> ReaderT Env IO ByteString
myReadFile fp = ReaderT $ \_env -> readFile fp

But having to do this regularly is tedious, and ties our code to a specific monad transformer stack. Instead, many of us would use MonadIO:

myReadFile :: MonadIO m => FilePath -> m ByteString
myReadFile = liftIO . readFile

But now let's play with a different function:

withBinaryFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a

We want a function with signature:

myWithBinaryFile
    :: FilePath
    -> IOMode
    -> (Handle -> ReaderT Env IO a)
    -> ReaderT Env IO a

If I squint hard enough, I can accomplish this directly with the ReaderT constructor via:

myWithBinaryFile fp mode inner =
  ReaderT $ \env -> withBinaryFile
    fp
    mode
    (\h -> runReaderT (inner h) env)

I dare you to try to and accomplish this with MonadIO and liftIO. It simply can't be done. (If you're looking for the technical reason, it's because IO appears in negative/argument position in withBinaryFile.)

However, with MonadUnliftIO, this is possible:

import Control.Monad.IO.Unlift

myWithBinaryFile
    :: MonadUnliftIO m
    => FilePath
    -> IOMode
    -> (Handle -> m a)
    -> m a
myWithBinaryFile fp mode inner =
  withRunInIO $ \runInIO ->
  withBinaryFile
    fp
    mode
    (\h -> runInIO (inner h))

That's it, you now know the entire basis of this library.

How common is this problem?

This pops up in a number of places. Some examples:

  • Proper exception handling, with functions like bracket, catch, and finally
  • Working with MVars via modifyMVar and similar
  • Using the timeout function
  • Installing callback handlers (e.g., do you want to do logging in a signal handler?).

This also pops up when working with libraries which are monomorphic on IO, even if they could be written more extensibly.

Examples

Reading through the codebase here is likely the best example to see how to use MonadUnliftIO in practice. And for many cases, you can simply add the MonadUnliftIO constraint and then use the pre-unlifted versions of functions (like UnliftIO.Exception.catch). But ultimately, you'll probably want to use the typeclass directly. The type class has only one method -- askUnliftIO:

newtype UnliftIO m = UnliftIO { unliftIO :: forall a. m a -> IO a }

class MonadIO m => MonadUnliftIO m where
  askUnliftIO :: m (UnliftIO m)

askUnliftIO gives us a function to run arbitrary computation in m in IO. Thus the "unlift": it's like liftIO, but the other way around.

Here are some sample typeclass instances:

instance MonadUnliftIO IO where
  askUnliftIO = return (UnliftIO id)
instance MonadUnliftIO m => MonadUnliftIO (IdentityT m) where
  askUnliftIO = IdentityT $
                withUnliftIO $ \u ->
                return (UnliftIO (unliftIO u . runIdentityT))
instance MonadUnliftIO m => MonadUnliftIO (ReaderT r m) where
  askUnliftIO = ReaderT $ \r ->
                withUnliftIO $ \u ->
                return (UnliftIO (unliftIO u . flip runReaderT r))

Note that:

  • The IO instance does not actually do any lifting or unlifting, and therefore it can use id
  • IdentityT is essentially just wrapping/unwrapping its data constructor, and then recursively calling withUnliftIO on the underlying monad.
  • ReaderT is just like IdentityT, but it captures the reader environment when starting.

We can use askUnliftIO to unlift a function:

timeout :: MonadUnliftIO m => Int -> m a -> m (Maybe a)
timeout x y = do
  u <- askUnliftIO
  System.Timeout.timeout x $ unliftIO u y

or more concisely using withRunIO:

timeout :: MonadUnliftIO m => Int -> m a -> m (Maybe a)
timeout x y = withRunInIO $ \run -> System.Timeout.timeout x $ run y

This is a common pattern: use withRunInIO to capture a run function, and then call the original function with the user-supplied arguments, applying run as necessary. withRunIO takes care of invoking unliftIO for us.

However, if we want to use the run function with different types, we must use askUnliftIO:

race :: MonadUnliftIO m => m a -> m b -> m (Either a b)
race a b = do
  u <- askUnliftIO
  liftIO (A.race (unliftIO u a) (unliftIO u b))

or more idiomatically withUnliftIO:

race :: MonadUnliftIO m => m a -> m b -> m (Either a b)
race a b = withUnliftIO $ \u -> A.race (unliftIO u a) (unliftIO u b)

This works just like withRunIO, except we use unliftIO u instead of run, which is polymorphic. You could get away with multiple withRunInIO calls here instead, but this approach is idiomatic and may be more performant (depending on optimizations).

And finally, a more complex usage, when unlifting the mask function. This function needs to unlift vaues to be passed into the restore function, and then liftIO the result of the restore function.

mask :: MonadUnliftIO m => ((forall a. m a -> m a) -> m b) -> m b
mask f = withUnliftIO $ \u -> Control.Exception.mask $ \unmask ->
  unliftIO u $ f $ liftIO . unmask . unliftIO u

Limitations

Not all monads which can be an instance of MonadIO can be instances of MonadUnliftIO, due to the MonadUnliftIO laws (described in the Haddocks for the typeclass). This prevents instances for a number of classes of transformers:

  • Transformers using continuations (e.g., ContT, ConduitM, Pipe)
  • Transformers with some monadic state (e.g., StateT, WriterT)
  • Transformers with multiple exit points (e.g., ExceptT and its ilk)

In fact, there are two specific classes of transformers that this approach does work for:

  • Transformers with no context at all (e.g., IdentityT, NoLoggingT)
  • Transformers with a context but no state (e.g., ReaderT, LoggingT)

This may sound restrictive, but this restriction is fully intentional. Trying to unlift actions in stateful monads leads to unpredictable behavior. For a long and exhaustive example of this, see A Tale of Two Brackets, which was a large motivation for writing this library.

Comparison to other approaches

You may be thinking "Haven't I seen a way to do catch in StateT?" You almost certainly have. Let's compare this approach with alternatives. (For an older but more thorough rundown of the options, see Exceptions and monad transformers.)

There are really two approaches to this problem:

  • Use a set of typeclasses for the specific functionality we care about. This is the approach taken by the exceptions package with MonadThrow, MonadCatch, and MonadMask. (Earlier approaches include MonadCatchIO-mtl and MonadCatchIO-transformers.)
  • Define a generic typeclass that allows any control structure to be unlifted. This is the approach taken by the monad-control package. (Earlier approaches include monad-peel and neither.)

The first style gives extra functionality in allowing instances that have nothing to do with runtime exceptions (e.g., a MonadCatch instance for Either). This is arguably a good thing. The second style gives extra functionality in allowing more operations to be unlifted (like threading primitives, not supported by the exceptions package).

Another distinction within the generic typeclass family is whether we unlift to just IO, or to arbitrary base monads. For those familiar, this is the distinction between the MonadIO and MonadBase typeclasses.

This package's main objection to all of the above approaches is that they work for too many monads, and provide difficult-to-predict behavior for a number of them (arguably: plain wrong behavior). For example, in lifted-base (built on top of monad-control), the finally operation will discard mutated state coming from the cleanup action, which is usually not what people expect. exceptions has different behavior here, which is arguably better. But we're arguing here that we should disallow all such ambiguity at the type level.

So comparing to other approaches:

monad-unlift

Throwing this one out there now: the monad-unlift library is built on top of monad-control, and uses fairly sophisticated type level features to restrict it to only the safe subset of monads. The same approach is taken by Control.Concurrent.Async.Lifted.Safe in the lifted-async package. Two problems with this:

  • The complicated type level functionality can confuse GHC in some cases, making it difficult to get code to compile.
  • We don't have an ecosystem of functions like lifted-base built on top of it, making it likely people will revert to the less safe cousin functions.

monad-control

The main contention until now is that unlifting in a transformer like StateT is unsafe. This is not universally true: if only one action is being unlifted, no ambiguity exists. So, for example, try :: IO a -> IO (Either e a) can safely be unlifted in StateT, while finally :: IO a -> IO b -> IO a cannot.

monad-control allows us to unlift both styles. In theory, we could write a variant of lifted-base that never does state discards, and let try be more general than finally. In other words, this is an advantage of monad-control over MonadUnliftIO. We've avoided providing any such extra typeclass in this package though, for two reasons:

  • MonadUnliftIO is a simple typeclass, easy to explain. We don't want to complicated matters (MonadBaseControl is a notoriously difficult to understand typeclass). This simplicity is captured by the laws for MonadUnliftIO, which make the behavior of the run functions close to that of the already familiar lift and liftIO.
  • Having this kind of split would be confusing in user code, when suddenly finally is not available to us. We would rather encourage good practices from the beginning.

Another distinction is that monad-control uses the MonadBase style, allowing unlifting to arbitrary base monads. In this package, we've elected to go with MonadIO style. This limits what we can do (e.g., no unlifting to STM), but we went this way because:

  • In practice, we've found that the vast majority of cases are dealing with IO
  • The split in the ecosystem between constraints like MonadBase IO and MonadIO leads to significant confusion, and MonadIO is by far the more common constraints (with the typeclass existing in base)

exceptions

One thing we lose by leaving the exceptions approach is the ability to model both pure and side-effecting (via IO) monads with a single paradigm. For example, it can be pretty convenient to have MonadThrow constraints for parsing functions, which will either return an Either value or throw a runtime exception. That said, there are detractors of that approach:

  • You lose type information about which exception was thrown
  • There is ambiguity about how the exception was returned in a constraint like (MonadIO m, MonadThrow m)

The latter could be addressed by defining a law such as throwM = liftIO . throwIO. However, we've decided in this library to go the route of encouraging Either return values for pure functions, and using runtime exceptions in IO otherwise. (You're of course free to also return IO (Either e a).)

By losing MonadCatch, we lose the ability to define a generic way to catch exceptions in continuation based monads (such as ConduitM). Our argument here is that those monads can freely provide their own catching functions. And in practice, long before the MonadCatch typeclass existed, conduit provided a catchC function.

In exchange for the MonadThrow typeclass, we provide helper functions to convert Either values to runtime exceptions in this package. And the MonadMask typeclass is now replaced fully by MonadUnliftIO, which like the monad-control case limits which monads we can be working with.

Async exception safety

The safe-exceptions package builds on top of the exceptions package and provides intelligent behavior for dealing with asynchronous exceptions, a common pitfall. This library provides a set of exception handling functions with the same async exception behavior as that library. You can consider this library a drop-in replacement for safe-exceptions. In the future, we may reimplement safe-exceptions to use MonadUnliftIO instead of MonadCatch and MonadMask.

Package split

The unliftio-core package provides just the typeclass with minimal dependencies (just base and transformers). If you're writing a library, we recommend depending on that package to provide your instances. The unliftio package is a "batteries loaded" library providing a plethora of pre-unlifted helper functions. It's a good choice for importing, or even for use in a custom prelude.

Orphans

The unliftio package currently provides orphan instances for types from the resourcet and monad-logger packages. This is not intended as a long-term solution; once unliftio is deemed more stable, the plan is to move those instances into the respective libraries and remove the dependency on them here.

If there are other temporary orphans that should be added, please bring it up in the issue tracker or send a PR, but we'll need to be selective about adding dependencies.

Future questions

  • Should we extend the set of functions exposed in UnliftIO.IO to include things like hSeek?
  • Are there other libraries that deserve to be unlifted here?

July 17, 2017 05:30 AM

Gabriel Gonzalez

Demystifying Haskell assignment

<html xmlns="http://www.w3.org/1999/xhtml"><head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/> <meta content="text/css" http-equiv="Content-Style-Type"/> <meta content="pandoc" name="generator"/> <style type="text/css">code{white-space: pre;}</style> <style type="text/css">div.sourceCode { overflow-x: auto; } table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode { margin: 0; padding: 0; vertical-align: baseline; border: none; } table.sourceCode { width: 100%; line-height: 100%; } td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; } td.sourceCode { padding-left: 5px; } code > span.kw { color: #007020; font-weight: bold; } /* Keyword */ code > span.dt { color: #902000; } /* DataType */ code > span.dv { color: #40a070; } /* DecVal */ code > span.bn { color: #40a070; } /* BaseN */ code > span.fl { color: #40a070; } /* Float */ code > span.ch { color: #4070a0; } /* Char */ code > span.st { color: #4070a0; } /* String */ code > span.co { color: #60a0b0; font-style: italic; } /* Comment */ code > span.ot { color: #007020; } /* Other */ code > span.al { color: #ff0000; font-weight: bold; } /* Alert */ code > span.fu { color: #06287e; } /* Function */ code > span.er { color: #ff0000; font-weight: bold; } /* Error */ code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */ code > span.cn { color: #880000; } /* Constant */ code > span.sc { color: #4070a0; } /* SpecialChar */ code > span.vs { color: #4070a0; } /* VerbatimString */ code > span.ss { color: #bb6688; } /* SpecialString */ code > span.im { } /* Import */ code > span.va { color: #19177c; } /* Variable */ code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */ code > span.op { color: #666666; } /* Operator */ code > span.bu { } /* BuiltIn */ code > span.ex { } /* Extension */ code > span.pp { color: #bc7a00; } /* Preprocessor */ code > span.at { color: #7d9029; } /* Attribute */ code > span.do { color: #ba2121; font-style: italic; } /* Documentation */ code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */ code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */ </style></head><body>

This post clarifies the distinction between <- and = in Haskell, which sometimes mystifies newcomers to the language. For example, consider the following contrived code snippet:

main = do
input <- getLine
let output = input ++ "!"
putStrLn output
putStrLn (input ++ "!")

The above program reads one line of input, and then prints that line twice with an exclamation mark at the end, like this:

$ ./example
Hello<Enter>
Hello!
Hello!

Why does the first line use the <- symbol to assign a value to input while the second line uses the = symbol to define output? Most languages use only one symbol to assign values (such as = or :=), so why does Haskell use two?

Equality

Haskell bucks the trend because the = symbol does not mean assignment and instead means something stronger than in most programming languages. Whenever you see an equality sign in a Haskell program that means that the two sides are truly equal. You can substitute either side of the equality for the other side and this substitution works in both directions.

For example, we define output to be equal (i.e. synonymous) with the expression input ++ "!" in our original program. This means that anywhere we see output in our program we can replace output with input ++ "!" instead, like this:

main = do
input <- getLine
let output = input ++ "!"
putStrLn (input ++ "!")
putStrLn (input ++ "!")

Vice versa, anywhere we see input ++ "!" in our program we can reverse the substitution and replace the expression with output instead, like this:

main = do
input <- getLine
let output = input ++ "!"
putStrLn output
putStrLn output

The language enforces that these sorts of substitutions do not change the behavior of our program (with caveats, but this is mostly true). All three of the above programs have the same behavior because we always replace one expression with another equal expression. In Haskell, the equality symbol denotes true mathematical equality.

Assignment

Once we understand equality we can better understand why Haskell uses a separate symbol for assignment: <-. For example, lets revisit this assignment in our original program:

main = do
-- Assign result of `getLine` to `input`
input <- getLine
...

input and getLine are not equal in any sense of the word. They don't even have the same type!

The type of input is String:

input :: String

... whereas the type of getLine is IO String:

getLine :: IO String

... which you can think of as "a subroutine whose return value is a String". We can't substitute either one for the other because we would get a type error. For example, if we substitute all occurrences of input with getLine we would get an invalid program which does not type check:

main = do
let output = getLine ++ "!" -- Type error!
putStrLn output
putStrLn (getLine ++ "!") -- Type error!

However, suppose we gloss over the type error and accept values of type IO String where the program expected just a String. Even then this substitution would still be wrong because our new program appears to request user input twice:

main = do
let output = getLine ++ "!" -- ← 1st request for input
putStrLn output
putStrLn (getLine ++ "!") -- ← 2nd request for input

Contrast this with our original program, which only asks for a single line of input and reuses the line twice:

main = do
input <- getLine -- ← 1st and only request for input
let output = input ++ "!"
putStrLn output
putStrLn (input ++ "!")

We cannot substitute the left-hand side of an assignment for the right-hand side of the assignment without changing the meaning of our program. This is why Haskell uses a separate symbol for assignment, because assignment does not denote equality.

Also, getLine and input are not even morally equal. getLine is a subroutine whose result may change every time, and to equate getLine with the result of any particular run doesn't make intuitive sense. That would be like calling the Unix ls command "a list of files".

Conclusion

Haskell has two separate symbols for <- and = because assignment and equality are not the same thing. Haskell just happens to be the first mainstream language that supports mathematical equality, which is why the language requires this symbolic distinction.

Language support for mathematical equality unlocks another useful language feature: equational reasoning. You can use more sophisticated equalities to formally reason about the behavior of larger programs, the same way you would reason about algebraic expressions in math.

</body></html>

by Gabriel Gonzalez (noreply@blogger.com) at July 17, 2017 01:02 AM

Jasper Van der Jeugt

ZuriHac plays

Introduction

This is a small write-up of a fun Haskell project that Andras Slemmer, Francesco Mazzoli and I worked on during ZuriHac 2017.

I work with Haskell professionally, and it can be hard to motivate myself to work on similar stuff during Hackathons. This year I also had a large part in organising the Hackathon, so there was little room to take on a serious project.

This is why I joined in the fun of of creating a deliberately silly thing during this Hackathon (aside from still trying to help people out as much as possible).

This year, we decided to implement something in the style of Twitch Plays Pokémon. Rather than picking a slow, turn-based game such as Pokémon, however, we wanted to try the same thing for a fast game, such as a platformer.

For the impatient, here is a quick preview:

The core design

The core design question of the project is how to handle keypresses and aggregate them when you have many concurrent users. Twitch Plays Pokémon solved this problem in two distinct modes:

  1. Anarchy mode (the default): any user keypress is sent directly to the game.

  2. Democracy mode: there is a voting window, after which the most popular user keypress is selected and sent to the game.

With a majority vote, players could switch between the modes.

There are a bunch of reasons why this does not work great for faster games:

  • Action games typically need you to hold a key for a certain amount of time, rather than just pressing and then releasing the key (e.g. Mario jumps higher if you hold jump for longer).

  • There’s little time to switch between modes in a fast-paced game.

  • Many games require you to press more than one key at the same time (e.g. jump and right in Mario).

We solved this by putting a key voting algorithm in place to aggregate the key events from the users. We think our algorithm should work well with most games, if the parameters are tweaked a bit.

First, imagine that we are looking at every key independently. For a given key, we might receive the following input from users, where a block means that the key is pressed:

We divide the time in sample intervals. The length of the sample interval can be tweaked per game. Let’s imagine it is 10ms for our example.

Every key press is expanded to match the sample interval first. This gives us something like:

We can aggregate them according to a treshold. This is another parameter that can be tweaked per game. In our example, we can set this treshold to 0.5. This means that 50% of users must be pressing a key before we consider it pressed. Concretely, for our 3 users, that means that at least two people must be pressing the key. This gives us the following aggregates:

After we’ve aggregated the key presses, we can send the result to the game. It’s important to note that this happens one sample interval after the actual user keypresses, since you are not easily able to make any conclusions before the interval has ended. This adds some latency but we didn’t find this a problem in practice for the games we tried.

Apart from that, we added to more complications to make the experience smoother:

  1. We look at all keys independently using the algorithm above, but before we decide on the final output, we take key groups into account.

    In Super Mario World, if you press left and right at the same time, Mario does not move. That is a problem: if the treshold i set to 0.2, 30% of people are pressing left, and 40% of people are pressing right, we would expect Mario to move right. However, using our naive algorithm, nothing happens.

    This is why we added key groups. A key group is a set of keys out of which at most one can be pressed. For example, {left, right} forms such a key group for Mario. We select the most popular key if there are multiple candidates within a group (right in the example).

  2. There is a timeout timer for activity per user. If the user does not press a key in a while, he is considered inactive, and this user is not counted towards the total amount of users. This prevents people from loading the page up but not participating from influencing the game too much.

The setup

That takes care of the key logic component, so now let’s look at the stack.

It’s all pretty self-explanatory:

  • Users open an HTML page which contains a JavaScript keylogger. We send the KeyPress and KeyRelease events to the server. On mobile, people can use a touchscreen interface which sends the same events.

  • The server runs the key voting algorithm we discussed before and sends the aggregated KeyPress and KeyRelease events to any connected sinks.

  • The main sink we implemented just executes the events using the XTestFakeInput call from xtest. In our case, the sink ran on the same machine as the server (my laptop).

We played Super Mario World with around 60 people on local Wi-Fi. We required 40% of people to press a key for the voting, in 10ms sampling windows. The system performed very smoothly, although the same cannot be said about the collaboration between users.

Thanks for joining in the fun! The code for our project can be found here.

<script async="async" charset="utf-8" src="https://platform.twitter.com/widgets.js"></script>

by Jasper Van der Jeugt at July 17, 2017 12:00 AM

July 16, 2017

Michael Snoyman

Some Upcoming Crazy Thoughts

I didn't mention it on my blog, but I put it on Twitter, so it's probably not a surprise to most. About five months ago, we had a baby boy (yay!). As you can imagine, new babies take a lot of energy, especially when it's your first baby of four to have colic. Many nights were spent walking Lavi around the block singing. I'm actually pretty lucky none of the neighbors called the cops, my singing definitely counts as disturbing the peace.

Pro tip to any new parents even a little bit superstitious: never say “We've been through all of this before, this baby can't surprise us.”

Anyway, I'm not wont to share personal anecdotes on this blog, but I mention this because I've obviously been pretty distracted with baby things. Fortunately, the baby is just about done with colic (just in time to start teething of course). Between that extra energy drain evaporating, having had lots of time to let my mind wander while walking a crying baby, and a few other things I'll detail at the end of this post, I've gotten to mentally explore some crazier ideas.

I've already been blogging a bit on fpcomplete.com about monad transformers. Expect some similar things on streaming data and polymorphism (perhaps) in the next few weeks. Also, I'll probably talk more about exceptions, though the thoughts there are less crazy and more reaffirming previous things.

A good question is why am I bothering with this blog post at all. I actually drafted most of it and then decided not to publish it for about a week. My thinking here is I don't want anyone taking my crazy thoughts too seriously. I like to explore ideas, and I explore ideas best by actually writing libraries and blog posts about them. In other words, I throw things at the wall and see what sticks. I usually buy into the idea completely for a bit to avoid second-guessing derailing an idea, and then take a step back afterwards to see if I like it.

Besides having reduced keyboard time for the past five months, here are some of the other stimuli leading to some of the ideas I'll be sharing:

  • I've spent considerably more effort on training. I've been doing documentation and tutorial writing for a while, but I've had multiple opportunities recently to train in a more direct setting. This has helped remind me of some of the newcomer experiences I've forgotten.

  • Similar to this, my time at LambdaConf earlier this year was great. My conference experiences usually are either non-functional programming conferences where I'm the Haskell anomaly, or advanced functional crowds. The huge mixture of experience levels with FP and Haskell at LambdaConf was wonderful and eye-opening (or perhaps reopening).

  • I've been working on a few projects where my major focus is on review and debugging, which forces me to focus less on making it easy to write code the first time, and more on writing code for maintainability and robustness (yeah, vague terms, don't beat me up over it).

  • Most recently, I did a major 10-day-straight hacking fest on the Stack code base, after not having seriously touched it for months (and the parts in question for over a year). I got to play with major refactorings and focuses on readability and future extensibility.

  • And in addition to all of this Haskell stuff, I've finally forced myself to start learning a new language for the first time in ten years. I went through quite the journey through programming languages before I hit on Haskell, and since then I've been so happy with it that I haven't wanted to touch anything else. But in the past half year, I've gotten into two languages to various extents:

    • PureScript This honestly wasn't much of a learning experience, since it's close enough to Haskell. I think GHCJS is a great project, and have enjoyed both Reflex and various React layers in it. But the smaller output and strict nature of PureScript make it something I wanted to experience for front end development.

    • Rust As I said on Twitter: "Rust is the first language I've learned in ten years (since Haskell) that both teaches new concepts and does stuff Haskell can't." Rust is an interesting language, promotes safety in a way that I like (the main reason I love Haskell to be honest), and has a really well designed community experience around it.

July 16, 2017 01:30 PM

July 15, 2017

Dan Piponi (sigfpe)

Self-referential logic via self-referential circuits


Introduction


TL;DR The behaviour of a certain kind of delay component has a formal similarity to Löb's theorem which gives a way to embed part of provability logic into electronic circuits.


Here's a famous paradoxical sentence:


This sentence is false


If it's false then it's true and if it's true then it's false.


Here's a paradoxical electronic circuit:


The component in the middle is an inverter. If the output of the circuit is high then its input is high and then it's output must be low, and vice versa.


There's a similarity here. But with a bit of tweaking you can turn the similarity into an isomorphism of sorts.


In the first case we avoid paradox by noting that in the mathematical frameworks commonly used by mathematicians it's impossible, in general, for a statement to assert it's own falsity. Instead, a statement can assert its own unprovability and then we get Gödel's incompleteness theorems and a statement that is apparently true and yet can't be proved.


In the second case we can't model the circuit straightforwardly as a digital circuit. In practice it might settle down to a voltage that lies between the official high and low voltages so we have to model it as an analogue circuit. Or instead we can introduce a clock and arrange that the feedback in the circuit is delayed. We then get an oscillator circuit that can be thought of as outputting a stream of bits.


The observation I want to make is that if the feedback delay is defined appropriately, these two scenarios are in some sense isomorphic. This means that we can model classic results about provability, like Gödel's incompleteness theorems, using electronic circuits. We can even use such circuits to investigate what happens when logicians or robots play games like Prisoner's Dilemma. I'll be making use of results found in Boolos' book on The Logic of Provability and some ideas I borrowed from Smoryński's paper on Fixed Point Algebras. I'll be assuming the reader has at least a slight acquaintance with ithe ideas behind provability logic.



Provability Logic

There are many descriptions of provability logic (aka GL) available online, so I'm not going to repeat it all here. However, I've put some background material in the appendix below and I'm going to give a very brief reminder now.


Start with (classical) propositional calculus which has a bunch of variables with names like and connectives like for AND, for OR, for NOT and for implication. (Note that .)


Provability logic extends propositional calculus by adding a unary operator . (I apologise, that's meant to be a □ but it's coming out like in LaTeX formulae. I think it's a bug in Google's LaTeX renderer.) The idea is that asserts that is provable in Peano Arithmetic, aka PA. In addition to the axioms of propositional calculus we have

and
as well as a rule that allows us to deduce from .


We also have this fixed point property:


Let be any predicate we can write in the language of GL involving the variable , and suppose that every appearance of in is inside a , e.g. . Then there is a fixed point, i.e. a proposition that makes no mention of such that is a theorem. In effect, for any such , is a proposition that asserts .


See the appendix for a brief mention of why we should expect this to be true.


From the fixed point property we can deduce Löb's theorem: . There is a proof at wikipedia that starts from the fixed point property.


We can also deduce the fixed point property from Löb's theorem so it's more usual to take Löb's theorem as an axiom of GL and show that the fixed point property follows. You can think of Löb's theorem as a cunning way to encode the fixed point property. In fact you can argue that it's a sort of Y-combinator, the function that allows the formation of recursive fixed points in functional programming languages. (That's also, sort of, the role played by the loeb function I defined way back. But note that loeb isn't really a proof of Löb's theorem, it just has formal similarities.)



Back to electronic circuits

In order to make digital circuits with feedback loops well-behaved I could introduce a circuit element that results in a delay of one clock cycle. If you insert one of these into the inverter circuit I started with you'll end up with an oscillator that flips back and forth between 0 and 1 on each clock cycle. But I want to work with something slightly stricter. I'd like my circuits to eventually stop oscillating. (I have an ulterior motive for studying these.) Let me introduce this component:


It is intended to serve as a delayed latch and I'll always have the flow of data being from left to right. The idea is that when it is switched on it outputs 1. It keeps outputting 1 until it sees a 0 input. When that happens, then on the next clock cycle its output drops to 0 and never goes back up to 1 until reset.


Because the output of our delay-latch isn't a function of its current input, we can't simply describe its operation as a mathematical function from to . Instead let's think of electronic components as binary operators on bitstreams, i.e. infinite streams of binary digits like ...00111010 with the digits emerging over time starting with the one written on the right and working leftwards. The ordinary logic gates perform bitwise operations which I'll represent using the operators in the C programming language. For example,

...001110 & ...101010 = ...001010
and
~...101 = ...010
and so on. Let's use □ to represent the effect of latch-delay on a bitstream. We have, for example,
□...000 = ...001
and
□...11101111 = ...00011111.
The operator □ takes the (possibly empty) contiguous sequence of 1's at the end of the bitstream, extends it by one 1, and sets everything further to the left to 0. If we restrict ourselves to bitstreams that eventually become all 0's or all 1's on the left, then bitstreams are in one-to-one correspondence with the integers using the twos complement representation. For example ...111111, all 1's, represents the number -1. I'll simply call the bistreams that represent integers integers. With this restriction we can use a classic C hacker trick to write □p=p^(p+1) where ^ is the C XOR operator. The operator □ outputs the bits that get flipped when you add one.


Let's use the symbol so that a → b is shorthand for ~a|b. Here are some properties of □:


1. □(-1) = -1


2. □p → □□p = -1


3. □(p → q) → □p → □q = -1


In addition we have the fixed point property:


Let F(p) be any function of p we can write using □ and the bitwise logical operators and such that all occurrences of p occur inside □. Then there is a unique bitstream q such that q=F(q).


We can make this clearer if we return to circuits. F(p) can be thought of as a circuit that takes p as input and outputs some value. We build the circuit using only boolean logic gates and delay-latch. We allow feedback loops, but only ones that go through delay-latches. With these restrictions it's pretty clear that the circuit is well-behaved and deterministically outputs a bitstream.


We also have the Löb property:


4. □(□p → p) → □p = -1


We can see this by examining the definition of □. Intuitively it says something like "once □ has seen a 0 input then no amount of setting input bits to 1 later in the stream make any different to its output".


I hope you've noticed something curious. These properties are extremely close to the properties of in GL. In fact, these electronic circuits form a model of the part of GL that doesn't involve variable names, i.e. what's known as letterless GL. We can formalise this:


1. Map to a wire set to 0, which outputs ...000 = 0.


2. Map to a wire set to 1, which outputs ...111 = -1.


3. Map , where is a binary connective, by creating a circuit that takes the outputs from the circuits for and and passes them into the corresponding boolean logic gate.


4. Map to the circuit for piped through a delay-latch.


For example, let's convert into a circuit. I'm translating to the circuit for .


I'm using red wires to mean wires carrying the value 1 rather than 0. I hope you can see that this circuit eventually settles into a state that outputs nothing but 1s.


We have this neat result:

Because delay-latch satisfies the same equations as in provability logic, any theorem, translated into a circuit, will produce a bistream of just 1s, i.e. -1.


But here's a more surprising result: the converse is true.

If the circuit corresponding to a letterless GL proposition produces a bistream of just 1s then the proposition is actually a theorem of GL.
I'm not going to prove this. (It's actually a disguised form of lemma 7.4 on p.95 of Boolos' book.) In the pictured example we got ...1111, so the circuit represents a theorem. As it represents Löb's theorem for the special case we should hope so. More generally, any bitstream that represents an integer can be converted back into a proposition that is equivalent to the original proposition. This means that bitstreams faithfully represent propositions of letterless GL. I'm not going to give the translation here but it's effectively given in Chapter 7 of Boolos. I'll use to represent the translation from propositions to bitstreams via circuits that I described above. Use to represent the translation of bitstream back into propositions. We have . But I haven't given a full description of and I haven't proved here that it has this property.



Circuits with feedback


In the previous section I considered letterless propositions of GL. When these are translated into circuits they don't have feedback loops. But we can also "solve equations" in GL using circuits with feedback. The GL fixed point theorem above says that we can "solve" the equation , with one letter , to produce a letterless proposition such that . Note here that is a letter in the language of GL. But I'm using to represent a proposition in letterless GL. If we build a circuit to represent , and feed its output back into where appears, then the output bitstream represents the fixed point. Here's a translation of the equation :

I'll let you try to convince yourself that such circuits always eventually output all 0's or all 1's. When we run the circuit we get the output ...1111000 = -8. As this is not -1 we know that the fixed point isn't a theorem. If I'd defined above you could use it to turn the bitstream back into a proposition.



The same, syntactically (optional section)

I have a Haskell library on github for working with GL: provability. This uses a syntactic approach and checks propositions for theoremhood using a tableau method. We can use it to analyse the above example with feedback. I have implemented a function, currently called value', to perform the evaluation of the bitstream for a proposition. However, in this case the fixedpoint function computes the fixed point proposition first and then converts to a bitstream rather than computing the bitstream directly from the circuit for F:



> let f p = Neg (Box p \/ Box (Box (Box p)))
> let Just p = fixedpoint f
> p
Dia T /\ Dia (Dia T /\ Dia (Dia T /\ Dia T))
> value' p
-8



(Note that Dia p means .)


The function fixedpoint does a lot of work under the hood. (It uses a tableau method to carry out Craig interpolation.) The circuit approach requires far less work.



Applications

1. Programs that reason about themselves


In principle we can write a program that enumerates all theorems of PA. That means we can use a quine trick to write a computer program that searches for a proof, in PA, of its own termination. Does such a program terminate?


We can answer this with Löb's theorem. Let "The program terminates". The program terminates if it can prove its termination. Formally this means we assume . Using one of the derivation rules of GL we get . Löb's theorem now gives us . Feed that back into our original hypothesis and we get . In other words, we deduce that our program does in fact terminate. (Thanks to Sridhar Ramesh for pointing this out to me.)


But we can deduce this using a circuit. We want a solution to . Here's the corresponding circuit:

It starts by outputting 1's and doesn't stop. In other words, the fixed point is a theorem. And that tells us is a theorem. And hence that the program terminates.


2. Robots who reason about each others play in Prisoner's Dilemma


For the background to this problem see Robust Cooperation in the Prisoner's Dilemma at LessWrong. We have two robot participants and playing Prisoner's Dilemma. Each can examine the other's source code and can search for proofs that the opponent will cooperate. Suppose each robot is programmed to enumerate all proofs of PA and cooperate if it finds a proof that its opponent will cooperate. Here we have "A will cooperate" and "B will cooperate". Our assumptions about the behaviour of the robots are and , and hence that . This corresponds to the circuit:

This outputs ...1111 = -1 so we can conclude and hence that these programs will cooperate. (Note that this doesn't work out nicely if robot B has a program that doesn't terminate but whose termination isn't provable in the formal system A is using. That means this approach is only good for robots that want to cooperate and want to confirm such cooperation. See the paper for more on this.)


At this point I really must emphasise that these applications are deceptively simple. I've shown how these simple circuits can answer some tricky problems about provability. But these aren't simply the usual translations from boolean algebra to logic gates. They work because circuits with delay-latch provide a model for letterless provability logic and that's only the case because of a lot of non-trivial theorem proving in Boolos that I haven't reproduced here. You're only allowed to use these simple circuits once you've seen the real proofs :-)



Things I didn't say above

1. I described the translation from propositions to circuits that I called above. But I didn't tell you what looks like. I'll leave this as an exercise. (Hint: consider the output from the translation of into a circuit.)


2. The integers, considered as bistreams, with the bitwise operators, and the unary operator □p=p^(p+1), form an algebraic structure. For example, if we define ⋄p=~□~p we have a Magari algebra. Structures like these are intended to capture the essential parts of self-referential arguments in an algebraic way.


3. Because of the interpretation of □ as a delayed latch in a circuit you could view it as saying "my input was always true until a moment ago". This surely embeds provability logic in a temporal logic of some sort.


4. (Deleted speculations about tit-for-tat that need rethinking.)


5. For even the most complex letterless proposition in Boolos you could check its theoremhood with a pretty small circuit. You could even consider doing this with a steam powered pneumatic circuit. I had to say that to fulfil a prophecy and maintain the integrity of the timeline.



Appendix on provability

The modern notion of a proof is that it is a string of symbols generated from some initial strings called "axioms" and some derivation rules that make new strings from both axioms and strings you've derived previously. Usually we pick axioms that represent "self-evident" truths and we pick derivation rules that are "truth-preserving" so that every proof ends at a true proposition of which it is a proof. The derivation rules are mechanical in nature: things like "if you have this symbol here and that symbol there then you can replace this symbol with that string you derived earlier" etc.


You can represent strings of symbols using numbers, so-called Gödel numbers. Let's pick a minimal mathematical framework for working with numbers: Peano Arithmetic, aka PA. Let's assume we've made some choice of Gödel numbering scheme and when is a proposition, write for the number representing . You can represent the mechanical derivation rules as operations on numbers. And that makes it possible to define a mathematical predicate that is true if and only if its argument represents a provable proposition.


In other words, we can prove using PA if and only if is a proposition provable in PA.


The predicate has some useful properties:


1.If we can prove , then we can prove .


We take the steps we used to prove , and convert everything to propositions about numbers. If is defined correctly then we can convert that sequence of numbers into a sequence of propositions about those numbers that makes up a proof of .


2. and imply


A fundamental step in any proof is modus ponens, i.e. that and implies . If does its job correctly then it had better know about this.


3. implies


One way is to prove this is to use Löb's theorem.


4.


The trivially true statement had better be provable or is broken.


Constructing is conceptually straightforward but hard work. I'm definitely not going to do it here.


And there's one last thing we need: self-reference. If is a proposition, how can we possibly assert without squeezing a copy of inside ? I'm not going to do that here either - just mention that we can use a variation of quining to achieve this. That allows us to form a proposition for which we can prove . In fact, we can go further. We can find propositions that solve for any predicate built from the usual boolean operations and as long as all of the occurrences of are inside the appearances of . Even though we can't form a proposition that directly asserts its own falsity, we can form one that asserts that it is unprovable, or one that asserts that you can't prove that you can't prove that you can prove it, or anything along those lines.


Anyway, all that and business is a lot of hassle. Provability logic, also known as GL, is intended to capture specifically the parts of PA that relate to provability. GL is propositional calculus extended with the provability operator . The intention is that if is a proposition, is a proposition in GL that represents in PA. The properties of above become the axioms and derivation rules of GL in the main text.

by Dan Piponi (noreply@blogger.com) at July 15, 2017 04:09 PM

July 12, 2017

Philip Wadler

Today's the day: Fight for Net Neutrality

<iframe allowfullscreen="true" class="b-hbp-video b-uploaded" frameborder="0" height="266" mozallowfullscreen="true" src="https://www.blogger.com/video.g?token=AD6v5dy79mMpW8uOFI5Re1GxoUvVr6PjvOoMUsXGtx8pHz6P8LSj8agIfPlQg6c7VwPxS5ED-6Na-jZeXDE" webkitallowfullscreen="true" width="320">

Today is an internet-wide day of action to support Net Neutrality.

Net Neutrality is under severe attack by the FCC and the Trump Administration. Only sustained action will save it. And if it falls in the US, will it be long before the rest of the world follows?

Net Neutrality is already being eroded. Virgin proudly tells me that I'm not charged mobile bandwidth when I use Twitter; other providers offer similar services for Facebook or Netflix. Seemingly a bonus, these offers are really a minus: they lock in the present winners, and make it difficult for the next generation of innovations to emerge.

Unless we act now, people will look back on our days as 'The Golden Age of the Internet'.

Do something now! It takes less than ten minutes. Details here.

by Philip Wadler (noreply@blogger.com) at July 12, 2017 09:49 AM

July 08, 2017

Holden Karau

Spark Testing Base new version and g8 template for Spark!

My yak shaving today lead me to release a new version of spark-testing-base (0.7.0) base with 2.1.1 support and improved python support as well as a g8 template for Spark projects so you don't always have to copy that build file from the last one and tweak it a bit.

On happy notes, RC6 for Spark 2.2 also passed today so I'll be publishing a new version of spark-testing-base next week after the release is finished.

by Holden Karau (noreply@blogger.com) at July 08, 2017 02:17 AM

July 07, 2017

Wolfgang Jeltsch

Haskell in Leipzig 2017 seeking contributions

Haskell in Leipzig (HaL) is taking place again from October 26 to October 28, 2017 at HTWK Leipzig. If you have any interesting Haskell-related material to share, please consider submitting an extended abstract.

About

Haskell is a modern functional programming language that allows rapid development of robust and correct software. It is renowned for its expressive type system, its unique approaches to concurrency and parallelism, and its excellent refactoring capabilities. Haskell is both the playing field of cutting-edge programming language research and a reliable base for commercial software development.

The workshop series Haskell in Leipzig (HaL), now in its 12th year, brings together Haskell developers, Haskell researchers, Haskell enthusiasts, and Haskell beginners to listen to talks, take part in tutorials, join in interesting conversations, and hack together. To support the latter, HaL will include a one-day hackathon this year. The workshop will have a focus on functional reactive programming (FRP) this time, while continuing to be open to all aspects of Haskell. As in the previous year, the workshop will be in English.

Contributions

Everything related to Haskell is on topic, whether it is about current research, practical applications, interesting ideas off the beaten track, education, or art, and topics may extend to functional programming in general and its connections to other programming paradigms.

Contributions can take the form of

  • talks (about 30 minutes),
  • tutorials (about 90 minutes),
  • demonstrations, artistic performances, or other extraordinary things.

Please submit an abstract that describes the content and form of your presentation, the intended audience, and required previous knowledge. We recommend a length of 2 pages, so that the program committee and the audience get a good idea of your contribution, but this is not a hard requirement.

Please submit your abstract as a PDF document via EasyChair until Friday, August 4, 2017. You will be notified by Friday, August 25, 2017.

Hacking Projects

Projects for the hackathon can be presented during the workshop. A prior submission is not needed for this.

Invited Speaker

  • Ivan Perez, University of Nottingham, UK

Program Committee

  • Edward Amsden, Plow Technologies, USA
  • Heinrich Apfelmus, Germany
  • Jurriaan Hage, Utrecht University, The Netherlands
  • Petra Hofstedt, BTU Cottbus-Senftenberg, Germany
  • Wolfgang Jeltsch, Tallinn University of Technology, Estonia (chair)
  • Andres Löh, Well-Typed LLP, Germany
  • Keiko Nakata, SAP SE, Germany
  • Henrik Nilsson, University of Nottingham, UK
  • Ertuğrul Söylemez, Intelego GmbH, Germany
  • Henning Thielemann, Germany
  • Niki Vazou, University of Maryland, USA
  • Johannes Waldmann, HTWK Leipzig, Germany

Tagged: conference, FRP, functional programming, Haskell

by Wolfgang Jeltsch at July 07, 2017 11:51 PM

Douglas M. Auclair (geophf)

June 2017 1HaskellADay 1Liners

  • June 17th, 2017:
    f :: (a, [a]) -> [a] -> [a]
    f (c, w1) w2 = c:w1 ++ w2

    Define f points-free
    • bazzargh @bazzargh (++).uncurry(:)
      • Felt there must be a nicer way to exploit symmetry of mappend.uncurry(mappend.pure) but can't find it

by geophf (noreply@blogger.com) at July 07, 2017 12:43 PM

July 06, 2017

Neil Mitchell

HaskellX Bytes talk next Tuesday

I'm talking about "Static Analysis in Haskell" at HaskellX Bytes next Tuesday (11th July), in London UK. Registration is free. The abstract is:

Haskell is a strongly typed programming language, which should be well suited to static analysis - specifically any insights about the program which don't require running the program. Alas, while type systems are becoming increasingly powerful, other forms of static analysis aren't advancing as fast. In this talk we'll give an overview of some of the forms of non-type-based static analysis that do exist, from the practical (GHC warnings, HLint, Weeder) to the research (LiquidHaskell, Catch).

I'm a big fan of static analysis, so this will be part summary, part sales pitch, and part call to arms. Followed by beer.

Clarification: I originally got the day wrong, and the url persists the original incorrect day. The talk is on Tuesday 11th July.

by Neil Mitchell (noreply@blogger.com) at July 06, 2017 08:09 PM

Joachim Breitner

The Micro Two Body Problem

Inspired by recent PhD comic “Academic Travel” and not-so-recent xkcd comic “Movie Narrative Charts”, I created the following graphics, which visualizes the travels of an academic couple over the course of 10 months (place names anonymized).

Two bodies traveling the world

Two bodies traveling the world

by Joachim Breitner (mail@joachim-breitner.de) at July 06, 2017 03:27 PM

July 05, 2017

Functional Jobs

Full-Stack Developer (Clojure) at Privacy Company (Full-time)

About the job

Full time position in our office in The Hague, The Netherlands

Privacy Company helps businesses solve the privacy compliance problem, with a focus on bringing privacy awareness within reach of those who are not experts or lawyers. Our web application offers a very practical approach to reach this goal and has been designed to make privacy management easier, more efficient and more fun.

The development team currently consists of two developers and one designer, and we are looking for a new developer who can help us expand the product further. Given the small size of the team, there are countless opportunities to make an impact on both the product and our internal culture.

Our ideal colleague shares our passion for writing clean and modular code, enjoys (or at least is interested in learning) Functional Programming, and has been writing code for fun for years. Our technology stack of choice is Clojure for the backend and ClojureScript for the frontend (with React). We find that Clojure’s simplicity, immutability and data-first approach results in clearer code that is easy to reason about and to test. In contrast, we're not fans of OOP, XML and Design Patterns.

What we expect from you

  • Experience with Clojure gets you major bonus points.
  • Experience with other functional programming languages (like Haskell, Common Lisp, Erlang, etc.) is always appreciated.
  • You understand practical demands, but still strive to do things the right way.
  • You care about understanding problems at their root, with all the attention and dedication it requires.
  • You are a "full-stack" developer: you like to work on anything from database queries and backend code to fine tuning CSS and building React components.
  • You are familiar with command line tools: you know your way around git, bash/zsh/etc, grep (and perhaps, why not, the occasional perl one-liner).
  • You are comfortable working with a database without an ORM.
  • Formal Computer Science education is not a hard requirement, relevant education can be a plus, but we are more interested in what you have built than what you have learned.

What we offer

  • A challenging, fun environment with lots of autonomy and self-direction
  • No hierarchy or project managers (we prefer self-organising)
  • Flexibility about when and where you work (however this is not a fully remote position, you should spend at least 1 day / week in the office with us)
  • You get to choose if you want a Mac or Linux laptop (or Windows, but why?)
  • Salary commensurate with your experience (32-40 hours/week)
  • Long term commitment is intended

Note to International Applicants: Unfortunately we cannot sponsor Visas to the Netherlands, please apply only if you have a valid EU work permit.

Get information on how to apply for this position.

July 05, 2017 01:38 PM

Michael Snoyman

The Spiderman Principle

With great power comes great responsibility

I was recently reminded of a bit of a mantra that I had at LambdaConf this year in discussions, and I decided to share it here. I received a bunch of questions like these (I'd share the originals, but I have a terrible memory):

  • Why is there no tutorial for doing X?
  • Why doesn't a library like Y exist?
  • Why has no one created a mailing list/discussion forum/etc for topic Z?

The answer to all of these is the same: because you haven't done it! And I don't mean that in the plural "you" or figuratively. The one and only reason why things don't get done is because you, personally, individually, have not done them.

This of course isn't literally true. There's a possibility that someone else will step up to the plate first. And there are limited numbers of hours in the day, and no one person can accomplish everything. But this mindset is, in my opinion, the only correct one to adopt if you want things to happen. It's your responsibility to do it; don't wait for others to do it.

You may have some legitimate objections to this philosophy:

  • How can I write a tutorial, I don't understand how to accomplish this?

    • Go ahead and write it as best you can, and ask people to review it. People are usually quite happy to provide corrections and advice.
    • A streamlined way of doing this is to send a pull request to an existing repo holding documentation (e.g., haskell-lang).
    • Worst case scenario: ask questions. Encourage people to write up answers. Volunteer to compose the answers into a coherent document at the end. Even people not willing to participate in writing a full tutorial themselves may be quite happy to answer direct questions, especially knowing their work will be preserved for others.
  • How can I write such a library, it's beyond my capabilities?

    • You'd be surprised about that. Give it a shot. Worst case scenario: it'll be a learning experience and otherwise an epic failure. Best case scenario: you succeed. Either way, it's a win-win situation.
    • Maybe your desired functionality fits into an existing library. Library authors tend to be quite happy to review and accept pull requests, and contributing an extra function can be less intimidating than writing a new library. (But please consider opening an issue first.)
    • And if you're certain you're not up to the task: try to encourage others. You may not succeed. But try to make the case for why this project is useful, interesting, necessary, or whatever other adjectives you believe apply. Motivate people.
  • I'm not a community leader, how can I encourage discussions?

    • There's no such thing as an "official" community leader. There are people with moderator access on some forums or control over certain websites. But that's not what makes someone a leader. If people want to hear what you have to say and join the conversation, you're leading a conversation.
    • Besides, you don't need to be a leader to start a discussion.
    • A slight retraction to all of this: if a topic has already been beaten to death, it's almost certainly not worth rehashing it. Reraising controversial points constantly doesn't help anyone.
  • It doesn't seem like the community agrees on this point, how can I advocate it?

    • Just because many people seem to be advocating X does not mean that it is universally held. There are many reasons why X seems to be the dominant viewpoint:

      • People may be legitimately unaware of alternatives
      • The people who disagree with X all think it's not worth speaking against the "dominant" opinion
      • The people who believe X are simply more passionate about it than those that don't.
    • So what if people disagree? Having healthy technical debate is a good thing. There are at least three outcomes I can see from such a debate:

      • You realize you were wrong
      • People disagreeing with you realize they were wrong
      • Both sides continue with their beliefs, but have a deeper understanding of both their positions and the alternatives
    • But again, try to avoid beating a topic to death

I don't know if people outside the Haskell world experience this as much as we do. But I've certainly seen a strong sentiment of "not being worthy" or some other such idea. It's rubbish. Join the conversation, lead the way, make things happen. The world will be better for it.

July 05, 2017 03:00 AM

July 04, 2017

Douglas M. Auclair (geophf)

June 2017 1HaskellADay Problems and Solutions

by geophf (noreply@blogger.com) at July 04, 2017 02:29 AM

July 03, 2017

Ken T Takusagawa

[zwhnekne] A generated multiplication table of the icosahedral rotation group

William Rowan Hamilton defined his Icosian Calculus with the following 3 relations on generators (a presentation of a group, a novel idea at the time): i^2=1, k^3=1, (ik)^5=1.

Incidentally, "discovering the Icosian generators" sounds like a phrase out of science fiction.

We computationally generated 60 elements from the relations above by pure algebraic manipulation of symbols, not relating strings of symbols to geometric meanings corresponding to rotations of a regular icosahedron.

The following are the 60 "minimal" elements.  Minimality, a way of choosing a canonical string, was first preferring shorter strings, then lexicographically ordering i before k.  The identity is denoted by 1.

1 i k ik ki kk iki ikk kik kki ikik ikki kiki kikk kkik ikiki ikikk ikkik kikik kikki kkiki kkikk ikikik ikikki ikkiki ikkikk kikikk kikkik kkikik ikikikk ikikkik ikkikik kikikki kikkiki kikkikk kkikikk ikikikki ikikkiki ikikkikk ikkikikk kikikkik kikkikik kkikikki ikikikkik ikikkikik ikkikikki kikikkiki kikikkikk kikkikikk kkikikkik ikikikkiki ikikikkikk ikikkikikk ikkikikkik kikkikikki kkikikkiki ikikkikikki ikkikikkiki kikkikikkik ikikkikikkik

We give the entire 60 by 60 multiplication table of these elements at the very bottom.  First, some highlights.

The following are the inverse relations.  Although the group is not commutative, the inverse relations are; that is, the left inverses are always equal to the right inverses.  (This is apparently true of all groups, or more precisely, if the group axioms are (seemingly) weakened to only require left inverses, it can be proved that a left inverse is always a right inverse and vice versa using only the weakened axioms, according to Wikipedia, citing "Algebra" by Serge Lang.)

1*1=1 i*i=1 k*kk=1 ik*kki=1 ki*ikk=1 kk*k=1 iki*ikki=1 ikk*ki=1 kik*kkikk=1 kki*ik=1 ikik*ikikik=1 ikki*iki=1 kiki*ikkikk=1 kikk*kikk=1 kkik*kkik=1 ikiki*kikik=1 ikikk*kikki=1 ikkik*kkiki=1 kikik*ikiki=1 kikki*ikikk=1 kkiki*ikkik=1 kkikk*kik=1 ikikik*ikik=1 ikikki*ikikki=1 ikkiki*ikkiki=1 ikkikk*kiki=1 kikikk*kikkikk=1 kikkik*kkikikk=1 kkikik*ikikikk=1 ikikikk*kkikik=1 ikikkik*kkikikki=1 ikkikik*ikikikki=1 kikikki*ikikkikk=1 kikkiki*ikkikikk=1 kikkikk*kikikk=1 kkikikk*kikkik=1 ikikikki*ikkikik=1 ikikkiki*ikkikikki=1 ikikkikk*kikikki=1 ikkikikk*kikkiki=1 kikikkik*ikikikkiki=1 kikkikik*ikikikkikk=1 kkikikki*ikikkik=1 ikikikkik*ikikikkik=1 ikikkikik*ikikkikik=1 ikkikikki*ikikkiki=1 kikikkiki*kikikkiki=1 kikikkikk*kikikkikk=1 kikkikikk*kikkikikk=1 kkikikkik*kkikikkik=1 ikikikkiki*kikikkik=1 ikikikkikk*kikkikik=1 ikikkikikk*kikkikikki=1 ikkikikkik*kkikikkiki=1 kikkikikki*ikikkikikk=1 kkikikkiki*ikkikikkik=1 ikikkikikki*ikikkikikki=1 ikkikikkiki*ikkikikkiki=1 kikkikikkik*kikkikikkik=1 ikikkikikkik*ikikkikikkik=1

These are the 16 square roots of unity (identity):

1 i kikk kkik ikikki ikkiki ikikikkik ikikkikik kikikkiki kikikkikk kikkikikk kkikikkik ikikkikikki ikkikikkiki kikkikikkik ikikkikikkik

The algorithm to find the 60 minimal elements was as follows: start with {1,i,k} and multiply all pairs and simplify each product as much as possible.  Collect the distinct products into a new set and multiply all pairs again, simplify, and repeat until the set converges onto a fixed point.  If the simplification technique is not powerful enough, it does not converge; instead the sets grow in size without bound.  We found a simplification technique ("repeat_best" in the source code) that converged to a set of 62 elements.

In the course of the calculations, we found the following 45 identities useful.  These were derived by starting with the third relation, ikikikikik=1, then left- or right-multiplying successively by k or i, gradually making the left-hand side smaller by annihilating i's and k's by the first and second relations.

ikikikikik=1 ikikikikikk=k ikikikiki=kk ikikikik=kki ikikikikk=kkik ikikiki=kkikk ikikik=kkikki ikikikk=kkikkik ikiki=kkikkikk ikik=kkikkikki ikikk=kkikkikkik iki=kkikkikkikk ik=kkikkikkikki ikk=kkikkikkikkik i=kkikkikkikkikk 1=kkikkikkikkikki 1=ikkikkikkikkikk kk=ikkikkikkikkik 1=kikkikkikkikkik k=ikkikkikkikki kk=kikkikkikkikki ki=ikkikkikkikk kki=kikkikkikkikk kikk=ikkikkikkik kkikk=kikkikkikkik kik=ikkikkikki kkik=kikkikkikki kiki=ikkikkikk kkiki=kikkikkikk kikikk=ikkikkik kkikikk=kikkikkik kikik=ikkikki kkikik=kikkikki kikiki=ikkikk kkikiki=kikkikk kikikikk=ikkik kkikikikk=kikkik kikikik=ikki kkikikik=kikki kikikiki=ikk kkikikiki=kikk kikikikikk=ik kkikikikikk=kik kikikikik=i kkikikikik=ki

One of the steps in developing simplification techniques was working on just the family of strings (kikki)^n, trying to simplify that infinite set down to a finite set.

We pruned the set of 62 down to 60 by applying a much more powerful, much slower simplification technique.  We applied up to N substitutions of the 45 identities above.  (This is roughly automatic theorem proving.)  The 45 identities can be applied in either direction, yielding 86 possibilities, not allowing a substitution from an empty string.  As a heuristic, we also excluded substitutions in which the left hand side was a single or two-character string.  This left 75 possibilities.  In the worst case, the running time is O(75^N), but on the average not that bad because most substitutions are not possible.  (Actually, the worst case running time is worse than O(75^N) because substitutions can make the string longer, providing more possible locations for the next substitution.)  N=6 was sufficient to find the 2 redundant elements among the 62.  We tried all the way up to N=9 to see if there were any more minimal versions of the 60 strings discovered at N=6, but there were none.  Source code in Haskell here.  (Incidentally, we originally started implementing this in Perl because Perl's regular expressions are nice for string matching and substitution, but we switched over to Haskell when the code became complicated.)

The task of proving two strings equal given a set of allowed substitutions is known as the "word problem".  This problem is in general Turing-undecidable.

Incidentally, if we allowed ourselves to associate a string the geometric meaning of a sequence of rotations of an icosahedron, then it would have been easy to discover two strings were equivalent because they would result in the same final orientation of the icosahedron.

One wonders how Hamilton proved that the order of his icosian calculus was actually 60, and how he proved it was isomorphic to the rotations of an icosahedron.  Incidentally, the calculations presented here do not prove that the order of the group generated by the 3 relations above is 60; they only prove that 60 is an upper bound.

Given the multiplication table for a group, we can construct a field.  Division might require linear algebra on 60x60 matrices.

The following is the multiplication table of the 60 elements.  We present it in this form, as straight text, not as an HTML table, because this is likely the easiest to parse by machine, and the only scenarios I can conceive of such a large table being useful all require first loading it into a machine.

1*1=1 1*i=i 1*k=k 1*ik=ik 1*ki=ki 1*kk=kk 1*iki=iki 1*ikk=ikk 1*kik=kik 1*kki=kki 1*ikik=ikik 1*ikki=ikki 1*kiki=kiki 1*kikk=kikk 1*kkik=kkik 1*ikiki=ikiki 1*ikikk=ikikk 1*ikkik=ikkik 1*kikik=kikik 1*kikki=kikki 1*kkiki=kkiki 1*kkikk=kkikk 1*ikikik=ikikik 1*ikikki=ikikki 1*ikkiki=ikkiki 1*ikkikk=ikkikk 1*kikikk=kikikk 1*kikkik=kikkik 1*kkikik=kkikik 1*ikikikk=ikikikk 1*ikikkik=ikikkik 1*ikkikik=ikkikik 1*kikikki=kikikki 1*kikkiki=kikkiki 1*kikkikk=kikkikk 1*kkikikk=kkikikk 1*ikikikki=ikikikki 1*ikikkiki=ikikkiki 1*ikikkikk=ikikkikk 1*ikkikikk=ikkikikk 1*kikikkik=kikikkik 1*kikkikik=kikkikik 1*kkikikki=kkikikki 1*ikikikkik=ikikikkik 1*ikikkikik=ikikkikik 1*ikkikikki=ikkikikki 1*kikikkiki=kikikkiki 1*kikikkikk=kikikkikk 1*kikkikikk=kikkikikk 1*kkikikkik=kkikikkik 1*ikikikkiki=ikikikkiki 1*ikikikkikk=ikikikkikk 1*ikikkikikk=ikikkikikk 1*ikkikikkik=ikkikikkik 1*kikkikikki=kikkikikki 1*kkikikkiki=kkikikkiki 1*ikikkikikki=ikikkikikki 1*ikkikikkiki=ikkikikkiki 1*kikkikikkik=kikkikikkik 1*ikikkikikkik=ikikkikikkik i*1=i i*i=1 i*k=ik i*ik=k i*ki=iki i*kk=ikk i*iki=ki i*ikk=kk i*kik=ikik i*kki=ikki i*ikik=kik i*ikki=kki i*kiki=ikiki i*kikk=ikikk i*kkik=ikkik i*ikiki=kiki i*ikikk=kikk i*ikkik=kkik i*kikik=ikikik i*kikki=ikikki i*kkiki=ikkiki i*kkikk=ikkikk i*ikikik=kikik i*ikikki=kikki i*ikkiki=kkiki i*ikkikk=kkikk i*kikikk=ikikikk i*kikkik=ikikkik i*kkikik=ikkikik i*ikikikk=kikikk i*ikikkik=kikkik i*ikkikik=kkikik i*kikikki=ikikikki i*kikkiki=ikikkiki i*kikkikk=ikikkikk i*kkikikk=ikkikikk i*ikikikki=kikikki i*ikikkiki=kikkiki i*ikikkikk=kikkikk i*ikkikikk=kkikikk i*kikikkik=ikikikkik i*kikkikik=ikikkikik i*kkikikki=ikkikikki i*ikikikkik=kikikkik i*ikikkikik=kikkikik i*ikkikikki=kkikikki i*kikikkiki=ikikikkiki i*kikikkikk=ikikikkikk i*kikkikikk=ikikkikikk i*kkikikkik=ikkikikkik i*ikikikkiki=kikikkiki i*ikikikkikk=kikikkikk i*ikikkikikk=kikkikikk i*ikkikikkik=kkikikkik i*kikkikikki=ikikkikikki i*kkikikkiki=ikkikikkiki i*ikikkikikki=kikkikikki i*ikkikikkiki=kkikikkiki i*kikkikikkik=ikikkikikkik i*ikikkikikkik=kikkikikkik k*1=k k*i=ki k*k=kk k*ik=kik k*ki=kki k*kk=1 k*iki=kiki k*ikk=kikk k*kik=kkik k*kki=i k*ikik=kikik k*ikki=kikki k*kiki=kkiki k*kikk=kkikk k*kkik=ik k*ikiki=ikkikk k*ikikk=kikikk k*ikkik=kikkik k*kikik=kkikik k*kikki=ikikik k*kkiki=iki k*kkikk=ikk k*ikikik=ikki k*ikikki=kikikki k*ikkiki=kikkiki k*ikkikk=kikkikk k*kikikk=kkikikk k*kikkik=ikikikk k*kkikik=ikik k*ikikikk=ikkik k*ikikkik=kikikkik k*ikkikik=kikkikik k*kikikki=kkikikki k*kikkiki=ikikikki k*kikkikk=ikiki k*kkikikk=ikikk k*ikikikki=ikkiki k*ikikkiki=kikikkiki k*ikikkikk=kikikkikk k*ikkikikk=kikkikikk k*kikikkik=kkikikkik k*kikkikik=ikikikkik k*kkikikki=ikikki k*ikikikkik=ikkikik k*ikikkikik=ikkikikki k*ikkikikki=kikkikikki k*kikikkiki=kkikikkiki k*kikikkikk=ikikikkiki k*kikkikikk=ikikikkikk k*kkikikkik=ikikkik k*ikikikkiki=ikikkikk k*ikikikkikk=ikkikikk k*ikikkikikk=ikkikikkik k*ikkikikkik=kikkikikkik k*kikkikikki=ikikkikik k*kkikikkiki=ikikkiki k*ikikkikikki=ikkikikkiki k*ikkikikkiki=ikikkikikkik k*kikkikikkik=ikikkikikk k*ikikkikikkik=ikikkikikki ik*1=ik ik*i=iki ik*k=ikk ik*ik=ikik ik*ki=ikki ik*kk=i ik*iki=ikiki ik*ikk=ikikk ik*kik=ikkik ik*kki=1 ik*ikik=ikikik ik*ikki=ikikki ik*kiki=ikkiki ik*kikk=ikkikk ik*kkik=k ik*ikiki=kkikk ik*ikikk=ikikikk ik*ikkik=ikikkik ik*kikik=ikkikik ik*kikki=kikik ik*kkiki=ki ik*kkikk=kk ik*ikikik=kki ik*ikikki=ikikikki ik*ikkiki=ikikkiki ik*ikkikk=ikikkikk ik*kikikk=ikkikikk ik*kikkik=kikikk ik*kkikik=kik ik*ikikikk=kkik ik*ikikkik=ikikikkik ik*ikkikik=ikikkikik ik*kikikki=ikkikikki ik*kikkiki=kikikki ik*kikkikk=kiki ik*kkikikk=kikk ik*ikikikki=kkiki ik*ikikkiki=ikikikkiki ik*ikikkikk=ikikikkikk ik*ikkikikk=ikikkikikk ik*kikikkik=ikkikikkik ik*kikkikik=kikikkik ik*kkikikki=kikki ik*ikikikkik=kkikik ik*ikikkikik=kkikikki ik*ikkikikki=ikikkikikki ik*kikikkiki=ikkikikkiki ik*kikikkikk=kikikkiki ik*kikkikikk=kikikkikk ik*kkikikkik=kikkik ik*ikikikkiki=kikkikk ik*ikikikkikk=kkikikk ik*ikikkikikk=kkikikkik ik*ikkikikkik=ikikkikikkik ik*kikkikikki=kikkikik ik*kkikikkiki=kikkiki ik*ikikkikikki=kkikikkiki ik*ikkikikkiki=kikkikikkik ik*kikkikikkik=kikkikikk ik*ikikkikikkik=kikkikikki ki*1=ki ki*i=k ki*k=kik ki*ik=kk ki*ki=kiki ki*kk=kikk ki*iki=kki ki*ikk=1 ki*kik=kikik ki*kki=kikki ki*ikik=kkik ki*ikki=i ki*kiki=ikkikk ki*kikk=kikikk ki*kkik=kikkik ki*ikiki=kkiki ki*ikikk=kkikk ki*ikkik=ik ki*kikik=ikki ki*kikki=kikikki ki*kkiki=kikkiki ki*kkikk=kikkikk ki*ikikik=kkikik ki*ikikki=ikikik ki*ikkiki=iki ki*ikkikk=ikk ki*kikikk=ikkik ki*kikkik=kikikkik ki*kkikik=kikkikik ki*ikikikk=kkikikk ki*ikikkik=ikikikk ki*ikkikik=ikik ki*kikikki=ikkiki ki*kikkiki=kikikkiki ki*kikkikk=kikikkikk ki*kkikikk=kikkikikk ki*ikikikki=kkikikki ki*ikikkiki=ikikikki ki*ikikkikk=ikiki ki*ikkikikk=ikikk ki*kikikkik=ikkikik ki*kikkikik=ikkikikki ki*kkikikki=kikkikikki ki*ikikikkik=kkikikkik ki*ikikkikik=ikikikkik ki*ikkikikki=ikikki ki*kikikkiki=ikikkikk ki*kikikkikk=ikkikikk ki*kikkikikk=ikkikikkik ki*kkikikkik=kikkikikkik ki*ikikikkiki=kkikikkiki ki*ikikikkikk=ikikikkiki ki*ikikkikikk=ikikikkikk ki*ikkikikkik=ikikkik ki*kikkikikki=ikkikikkiki ki*kkikikkiki=ikikkikikkik ki*ikikkikikki=ikikkikik ki*ikkikikkiki=ikikkiki ki*kikkikikkik=ikikkikikki ki*ikikkikikkik=ikikkikikk kk*1=kk kk*i=kki kk*k=1 kk*ik=kkik kk*ki=i kk*kk=k kk*iki=kkiki kk*ikk=kkikk kk*kik=ik kk*kki=ki kk*ikik=kkikik kk*ikki=ikikik kk*kiki=iki kk*kikk=ikk kk*kkik=kik kk*ikiki=kikkikk kk*ikikk=kkikikk kk*ikkik=ikikikk kk*kikik=ikik kk*kikki=ikki kk*kkiki=kiki kk*kkikk=kikk kk*ikikik=kikki kk*ikikki=kkikikki kk*ikkiki=ikikikki kk*ikkikk=ikiki kk*kikikk=ikikk kk*kikkik=ikkik kk*kkikik=kikik kk*ikikikk=kikkik kk*ikikkik=kkikikkik kk*ikkikik=ikikikkik kk*kikikki=ikikki kk*kikkiki=ikkiki kk*kikkikk=ikkikk kk*kkikikk=kikikk kk*ikikikki=kikkiki kk*ikikkiki=kkikikkiki kk*ikikkikk=ikikikkiki kk*ikkikikk=ikikikkikk kk*kikikkik=ikikkik kk*kikkikik=ikkikik kk*kkikikki=kikikki kk*ikikikkik=kikkikik kk*ikikkikik=kikkikikki kk*ikkikikki=ikikkikik kk*kikikkiki=ikikkiki kk*kikikkikk=ikikkikk kk*kikkikikk=ikkikikk kk*kkikikkik=kikikkik kk*ikikikkiki=kikikkikk kk*ikikikkikk=kikkikikk kk*ikikkikikk=kikkikikkik kk*ikkikikkik=ikikkikikk kk*kikkikikki=ikkikikki kk*kkikikkiki=kikikkiki kk*ikikkikikki=ikikkikikkik kk*ikkikikkiki=ikikkikikki kk*kikkikikkik=ikkikikkik kk*ikikkikikkik=ikkikikkiki iki*1=iki iki*i=ik iki*k=ikik iki*ik=ikk iki*ki=ikiki iki*kk=ikikk iki*iki=ikki iki*ikk=i iki*kik=ikikik iki*kki=ikikki iki*ikik=ikkik iki*ikki=1 iki*kiki=kkikk iki*kikk=ikikikk iki*kkik=ikikkik iki*ikiki=ikkiki iki*ikikk=ikkikk iki*ikkik=k iki*kikik=kki iki*kikki=ikikikki iki*kkiki=ikikkiki iki*kkikk=ikikkikk iki*ikikik=ikkikik iki*ikikki=kikik iki*ikkiki=ki iki*ikkikk=kk iki*kikikk=kkik iki*kikkik=ikikikkik iki*kkikik=ikikkikik iki*ikikikk=ikkikikk iki*ikikkik=kikikk iki*ikkikik=kik iki*kikikki=kkiki iki*kikkiki=ikikikkiki iki*kikkikk=ikikikkikk iki*kkikikk=ikikkikikk iki*ikikikki=ikkikikki iki*ikikkiki=kikikki iki*ikikkikk=kiki iki*ikkikikk=kikk iki*kikikkik=kkikik iki*kikkikik=kkikikki iki*kkikikki=ikikkikikki iki*ikikikkik=ikkikikkik iki*ikikkikik=kikikkik iki*ikkikikki=kikki iki*kikikkiki=kikkikk iki*kikikkikk=kkikikk iki*kikkikikk=kkikikkik iki*kkikikkik=ikikkikikkik iki*ikikikkiki=ikkikikkiki iki*ikikikkikk=kikikkiki iki*ikikkikikk=kikikkikk iki*ikkikikkik=kikkik iki*kikkikikki=kkikikkiki iki*kkikikkiki=kikkikikkik iki*ikikkikikki=kikkikik iki*ikkikikkiki=kikkiki iki*kikkikikkik=kikkikikki iki*ikikkikikkik=kikkikikk ikk*1=ikk ikk*i=ikki ikk*k=i ikk*ik=ikkik ikk*ki=1 ikk*kk=ik ikk*iki=ikkiki ikk*ikk=ikkikk ikk*kik=k ikk*kki=iki ikk*ikik=ikkikik ikk*ikki=kikik ikk*kiki=ki ikk*kikk=kk ikk*kkik=ikik ikk*ikiki=ikikkikk ikk*ikikk=ikkikikk ikk*ikkik=kikikk ikk*kikik=kik ikk*kikki=kki ikk*kkiki=ikiki ikk*kkikk=ikikk ikk*ikikik=ikikki ikk*ikikki=ikkikikki ikk*ikkiki=kikikki ikk*ikkikk=kiki ikk*kikikk=kikk ikk*kikkik=kkik ikk*kkikik=ikikik ikk*ikikikk=ikikkik ikk*ikikkik=ikkikikkik ikk*ikkikik=kikikkik ikk*kikikki=kikki ikk*kikkiki=kkiki ikk*kikkikk=kkikk ikk*kkikikk=ikikikk ikk*ikikikki=ikikkiki ikk*ikikkiki=ikkikikkiki ikk*ikikkikk=kikikkiki ikk*ikkikikk=kikikkikk ikk*kikikkik=kikkik ikk*kikkikik=kkikik ikk*kkikikki=ikikikki ikk*ikikikkik=ikikkikik ikk*ikikkikik=ikikkikikki ikk*ikkikikki=kikkikik ikk*kikikkiki=kikkiki ikk*kikikkikk=kikkikk ikk*kikkikikk=kkikikk ikk*kkikikkik=ikikikkik ikk*ikikikkiki=ikikikkikk ikk*ikikikkikk=ikikkikikk ikk*ikikkikikk=ikikkikikkik ikk*ikkikikkik=kikkikikk ikk*kikkikikki=kkikikki ikk*kkikikkiki=ikikikkiki ikk*ikikkikikki=kikkikikkik ikk*ikkikikkiki=kikkikikki ikk*kikkikikkik=kkikikkik ikk*ikikkikikkik=kkikikkiki kik*1=kik kik*i=kiki kik*k=kikk kik*ik=kikik kik*ki=kikki kik*kk=ki kik*iki=ikkikk kik*ikk=kikikk kik*kik=kikkik kik*kki=k kik*ikik=ikki kik*ikki=kikikki kik*kiki=kikkiki kik*kikk=kikkikk kik*kkik=kk kik*ikiki=ikk kik*ikikk=ikkik kik*ikkik=kikikkik kik*kikik=kikkikik kik*kikki=kkikik kik*kkiki=kki kik*kkikk=1 kik*ikikik=i kik*ikikki=ikkiki kik*ikkiki=kikikkiki kik*ikkikk=kikikkikk kik*kikikk=kikkikikk kik*kikkik=kkikikk kik*kkikik=kkik kik*ikikikk=ik kik*ikikkik=ikkikik kik*ikkikik=ikkikikki kik*kikikki=kikkikikki kik*kikkiki=kkikikki kik*kikkikk=kkiki kik*kkikikk=kkikk kik*ikikikki=iki kik*ikikkiki=ikikkikk kik*ikikkikk=ikkikikk kik*ikkikikk=ikkikikkik kik*kikikkik=kikkikikkik kik*kikkikik=kkikikkik kik*kkikikki=ikikik kik*ikikikkik=ikik kik*ikikkikik=ikikki kik*ikkikikki=ikkikikkiki kik*kikikkiki=ikikkikikkik kik*kikikkikk=kkikikkiki kik*kikkikikk=ikikikkiki kik*kkikikkik=ikikikk kik*ikikikkiki=ikiki kik*ikikikkikk=ikikk kik*ikikkikikk=ikikkik kik*ikkikikkik=ikikkikikki kik*kikkikikki=ikikikkik kik*kkikikkiki=ikikikki kik*ikikkikikki=ikikkiki kik*ikkikikkiki=ikikkikikk kik*kikkikikkik=ikikikkikk kik*ikikkikikkik=ikikkikik kki*1=kki kki*i=kk kki*k=kkik kki*ik=1 kki*ki=kkiki kki*kk=kkikk kki*iki=i kki*ikk=k kki*kik=kkikik kki*kki=ikikik kki*ikik=ik kki*ikki=ki kki*kiki=kikkikk kki*kikk=kkikikk kki*kkik=ikikikk kki*ikiki=iki kki*ikikk=ikk kki*ikkik=kik kki*kikik=kikki kki*kikki=kkikikki kki*kkiki=ikikikki kki*kkikk=ikiki kki*ikikik=ikik kki*ikikki=ikki kki*ikkiki=kiki kki*ikkikk=kikk kki*kikikk=kikkik kki*kikkik=kkikikkik kki*kkikik=ikikikkik kki*ikikikk=ikikk kki*ikikkik=ikkik kki*ikkikik=kikik kki*kikikki=kikkiki kki*kikkiki=kkikikkiki kki*kikkikk=ikikikkiki kki*kkikikk=ikikikkikk kki*ikikikki=ikikki kki*ikikkiki=ikkiki kki*ikikkikk=ikkikk kki*ikkikikk=kikikk kki*kikikkik=kikkikik kki*kikkikik=kikkikikki kki*kkikikki=ikikkikik kki*ikikikkik=ikikkik kki*ikikkikik=ikkikik kki*ikkikikki=kikikki kki*kikikkiki=kikikkikk kki*kikikkikk=kikkikikk kki*kikkikikk=kikkikikkik kki*kkikikkik=ikikkikikk kki*ikikikkiki=ikikkiki kki*ikikikkikk=ikikkikk kki*ikikkikikk=ikkikikk kki*ikkikikkik=kikikkik kki*kikkikikki=ikikkikikkik kki*kkikikkiki=ikikkikikki kki*ikikkikikki=ikkikikki kki*ikkikikkiki=kikikkiki kki*kikkikikkik=ikkikikkiki kki*ikikkikikkik=ikkikikkik ikik*1=ikik ikik*i=ikiki ikik*k=ikikk ikik*ik=ikikik ikik*ki=ikikki ikik*kk=iki ikik*iki=kkikk ikik*ikk=ikikikk ikik*kik=ikikkik ikik*kki=ik ikik*ikik=kki ikik*ikki=ikikikki ikik*kiki=ikikkiki ikik*kikk=ikikkikk ikik*kkik=ikk ikik*ikiki=kk ikik*ikikk=kkik ikik*ikkik=ikikikkik ikik*kikik=ikikkikik ikik*kikki=ikkikik ikik*kkiki=ikki ikik*kkikk=i ikik*ikikik=1 ikik*ikikki=kkiki ikik*ikkiki=ikikikkiki ikik*ikkikk=ikikikkikk ikik*kikikk=ikikkikikk ikik*kikkik=ikkikikk ikik*kkikik=ikkik ikik*ikikikk=k ikik*ikikkik=kkikik ikik*ikkikik=kkikikki ikik*kikikki=ikikkikikki ikik*kikkiki=ikkikikki ikik*kikkikk=ikkiki ikik*kkikikk=ikkikk ikik*ikikikki=ki ikik*ikikkiki=kikkikk ikik*ikikkikk=kkikikk ikik*ikkikikk=kkikikkik ikik*kikikkik=ikikkikikkik ikik*kikkikik=ikkikikkik ikik*kkikikki=kikik ikik*ikikikkik=kik ikik*ikikkikik=kikki ikik*ikkikikki=kkikikkiki ikik*kikikkiki=kikkikikkik ikik*kikikkikk=ikkikikkiki ikik*kikkikikk=kikikkiki ikik*kkikikkik=kikikk ikik*ikikikkiki=kiki ikik*ikikikkikk=kikk ikik*ikikkikikk=kikkik ikik*ikkikikkik=kikkikikki ikik*kikkikikki=kikikkik ikik*kkikikkiki=kikikki ikik*ikikkikikki=kikkiki ikik*ikkikikkiki=kikkikikk ikik*kikkikikkik=kikikkikk ikik*ikikkikikkik=kikkikik ikki*1=ikki ikki*i=ikk ikki*k=ikkik ikki*ik=i ikki*ki=ikkiki ikki*kk=ikkikk ikki*iki=1 ikki*ikk=ik ikki*kik=ikkikik ikki*kki=kikik ikki*ikik=k ikki*ikki=iki ikki*kiki=ikikkikk ikki*kikk=ikkikikk ikki*kkik=kikikk ikki*ikiki=ki ikki*ikikk=kk ikki*ikkik=ikik ikki*kikik=ikikki ikki*kikki=ikkikikki ikki*kkiki=kikikki ikki*kkikk=kiki ikki*ikikik=kik ikki*ikikki=kki ikki*ikkiki=ikiki ikki*ikkikk=ikikk ikki*kikikk=ikikkik ikki*kikkik=ikkikikkik ikki*kkikik=kikikkik ikki*ikikikk=kikk ikki*ikikkik=kkik ikki*ikkikik=ikikik ikki*kikikki=ikikkiki ikki*kikkiki=ikkikikkiki ikki*kikkikk=kikikkiki ikki*kkikikk=kikikkikk ikki*ikikikki=kikki ikki*ikikkiki=kkiki ikki*ikikkikk=kkikk ikki*ikkikikk=ikikikk ikki*kikikkik=ikikkikik ikki*kikkikik=ikikkikikki ikki*kkikikki=kikkikik ikki*ikikikkik=kikkik ikki*ikikkikik=kkikik ikki*ikkikikki=ikikikki ikki*kikikkiki=ikikikkikk ikki*kikikkikk=ikikkikikk ikki*kikkikikk=ikikkikikkik ikki*kkikikkik=kikkikikk ikki*ikikikkiki=kikkiki ikki*ikikikkikk=kikkikk ikki*ikikkikikk=kkikikk ikki*ikkikikkik=ikikikkik ikki*kikkikikki=kikkikikkik ikki*kkikikkiki=kikkikikki ikki*ikikkikikki=kkikikki ikki*ikkikikkiki=ikikikkiki ikki*kikkikikkik=kkikikkiki ikki*ikikkikikkik=kkikikkik kiki*1=kiki kiki*i=kik kiki*k=kikik kiki*ik=kikk kiki*ki=ikkikk kiki*kk=kikikk kiki*iki=kikki kiki*ikk=ki kiki*kik=ikki kiki*kki=kikikki kiki*ikik=kikkik kiki*ikki=k kiki*kiki=ikk kiki*kikk=ikkik kiki*kkik=kikikkik kiki*ikiki=kikkiki kiki*ikikk=kikkikk kiki*ikkik=kk kiki*kikik=i kiki*kikki=ikkiki kiki*kkiki=kikikkiki kiki*kkikk=kikikkikk kiki*ikikik=kikkikik kiki*ikikki=kkikik kiki*ikkiki=kki kiki*ikkikk=1 kiki*kikikk=ik kiki*kikkik=ikkikik kiki*kkikik=ikkikikki kiki*ikikikk=kikkikikk kiki*ikikkik=kkikikk kiki*ikkikik=kkik kiki*kikikki=iki kiki*kikkiki=ikikkikk kiki*kikkikk=ikkikikk kiki*kkikikk=ikkikikkik kiki*ikikikki=kikkikikki kiki*ikikkiki=kkikikki kiki*ikikkikk=kkiki kiki*ikkikikk=kkikk kiki*kikikkik=ikik kiki*kikkikik=ikikki kiki*kkikikki=ikkikikkiki kiki*ikikikkik=kikkikikkik kiki*ikikkikik=kkikikkik kiki*ikkikikki=ikikik kiki*kikikkiki=ikiki kiki*kikikkikk=ikikk kiki*kikkikikk=ikikkik kiki*kkikikkik=ikikkikikki kiki*ikikikkiki=ikikkikikkik kiki*ikikikkikk=kkikikkiki kiki*ikikkikikk=ikikikkiki kiki*ikkikikkik=ikikikk kiki*kikkikikki=ikikkiki kiki*kkikikkiki=ikikkikikk kiki*ikikkikikki=ikikikkik kiki*ikkikikkiki=ikikikki kiki*kikkikikkik=ikikkikik kiki*ikikkikikkik=ikikikkikk kikk*1=kikk kikk*i=kikki kikk*k=ki kikk*ik=kikkik kikk*ki=k kikk*kk=kik kikk*iki=kikkiki kikk*ikk=kikkikk kikk*kik=kk kikk*kki=kiki kikk*ikik=kikkikik kikk*ikki=kkikik kikk*kiki=kki kikk*kikk=1 kikk*kkik=kikik kikk*ikiki=kikikkikk kikk*ikikk=kikkikikk kikk*ikkik=kkikikk kikk*kikik=kkik kikk*kikki=i kikk*kkiki=ikkikk kikk*kkikk=kikikk kikk*ikikik=kikikki kikk*ikikki=kikkikikki kikk*ikkiki=kkikikki kikk*ikkikk=kkiki kikk*kikikk=kkikk kikk*kikkik=ik kikk*kkikik=ikki kikk*ikikikk=kikikkik kikk*ikikkik=kikkikikkik kikk*ikkikik=kkikikkik kikk*kikikki=ikikik kikk*kikkiki=iki kikk*kikkikk=ikk kikk*kkikikk=ikkik kikk*ikikikki=kikikkiki kikk*ikikkiki=ikikkikikkik kikk*ikikkikk=kkikikkiki kikk*ikkikikk=ikikikkiki kikk*kikikkik=ikikikk kikk*kikkikik=ikik kikk*kkikikki=ikkiki kikk*ikikikkik=ikkikikki kikk*ikikkikik=ikkikikkiki kikk*ikkikikki=ikikikkik kikk*kikikkiki=ikikikki kikk*kikikkikk=ikiki kikk*kikkikikk=ikikk kikk*kkikikkik=ikkikik kikk*ikikikkiki=ikkikikk kikk*ikikikkikk=ikkikikkik kikk*ikikkikikk=ikikkikikki kikk*ikkikikkik=ikikikkikk kikk*kikkikikki=ikikki kikk*kkikikkiki=ikikkikk kikk*ikikkikikki=ikikkikikk kikk*ikkikikkiki=ikikkikik kikk*kikkikikkik=ikikkik kikk*ikikkikikkik=ikikkiki kkik*1=kkik kkik*i=kkiki kkik*k=kkikk kkik*ik=kkikik kkik*ki=ikikik kkik*kk=kki kkik*iki=kikkikk kkik*ikk=kkikikk kkik*kik=ikikikk kkik*kki=kk kkik*ikik=kikki kkik*ikki=kkikikki kkik*kiki=ikikikki kkik*kikk=ikiki kkik*kkik=1 kkik*ikiki=kikk kkik*ikikk=kikkik kkik*ikkik=kkikikkik kkik*kikik=ikikikkik kkik*kikki=ikik kkik*kkiki=i kkik*kkikk=k kkik*ikikik=ki kkik*ikikki=kikkiki kkik*ikkiki=kkikikkiki kkik*ikkikk=ikikikkiki kkik*kikikk=ikikikkikk kkik*kikkik=ikikk kkik*kkikik=ik kkik*ikikikk=kik kkik*ikikkik=kikkikik kkik*ikkikik=kikkikikki kkik*kikikki=ikikkikik kkik*kikkiki=ikikki kkik*kikkikk=iki kkik*kkikikk=ikk kkik*ikikikki=kiki kkik*ikikkiki=kikikkikk kkik*ikikkikk=kikkikikk kkik*ikkikikk=kikkikikkik kkik*kikikkik=ikikkikikk kkik*kikkikik=ikikkik kkik*kkikikki=ikki kkik*ikikikkik=kikik kkik*ikikkikik=kikikki kkik*ikkikikki=ikikkikikkik kkik*kikikkiki=ikikkikikki kkik*kikikkikk=ikikkiki kkik*kikkikikk=ikikkikk kkik*kkikikkik=ikkik kkik*ikikikkiki=ikkikk kkik*ikikikkikk=kikikk kkik*ikikkikikk=kikikkik kkik*ikkikikkik=ikkikikkiki kkik*kikkikikki=ikkikik kkik*kkikikkiki=ikkiki kkik*ikikkikikki=kikikkiki kkik*ikkikikkiki=ikkikikkik kkik*kikkikikkik=ikkikikk kkik*ikikkikikkik=ikkikikki ikiki*1=ikiki ikiki*i=ikik ikiki*k=ikikik ikiki*ik=ikikk ikiki*ki=kkikk ikiki*kk=ikikikk ikiki*iki=ikikki ikiki*ikk=iki ikiki*kik=kki ikiki*kki=ikikikki ikiki*ikik=ikikkik ikiki*ikki=ik ikiki*kiki=kk ikiki*kikk=kkik ikiki*kkik=ikikikkik ikiki*ikiki=ikikkiki ikiki*ikikk=ikikkikk ikiki*ikkik=ikk ikiki*kikik=1 ikiki*kikki=kkiki ikiki*kkiki=ikikikkiki ikiki*kkikk=ikikikkikk ikiki*ikikik=ikikkikik ikiki*ikikki=ikkikik ikiki*ikkiki=ikki ikiki*ikkikk=i ikiki*kikikk=k ikiki*kikkik=kkikik ikiki*kkikik=kkikikki ikiki*ikikikk=ikikkikikk ikiki*ikikkik=ikkikikk ikiki*ikkikik=ikkik ikiki*kikikki=ki ikiki*kikkiki=kikkikk ikiki*kikkikk=kkikikk ikiki*kkikikk=kkikikkik ikiki*ikikikki=ikikkikikki ikiki*ikikkiki=ikkikikki ikiki*ikikkikk=ikkiki ikiki*ikkikikk=ikkikk ikiki*kikikkik=kik ikiki*kikkikik=kikki ikiki*kkikikki=kkikikkiki ikiki*ikikikkik=ikikkikikkik ikiki*ikikkikik=ikkikikkik ikiki*ikkikikki=kikik ikiki*kikikkiki=kiki ikiki*kikikkikk=kikk ikiki*kikkikikk=kikkik ikiki*kkikikkik=kikkikikki ikiki*ikikikkiki=kikkikikkik ikiki*ikikikkikk=ikkikikkiki ikiki*ikikkikikk=kikikkiki ikiki*ikkikikkik=kikikk ikiki*kikkikikki=kikkiki ikiki*kkikikkiki=kikkikikk ikiki*ikikkikikki=kikikkik ikiki*ikkikikkiki=kikikki ikiki*kikkikikkik=kikkikik ikiki*ikikkikikkik=kikikkikk ikikk*1=ikikk ikikk*i=ikikki ikikk*k=iki ikikk*ik=ikikkik ikikk*ki=ik ikikk*kk=ikik ikikk*iki=ikikkiki ikikk*ikk=ikikkikk ikikk*kik=ikk ikikk*kki=ikiki ikikk*ikik=ikikkikik ikikk*ikki=ikkikik ikikk*kiki=ikki ikikk*kikk=i ikikk*kkik=ikikik ikikk*ikiki=ikikikkikk ikikk*ikikk=ikikkikikk ikikk*ikkik=ikkikikk ikikk*kikik=ikkik ikikk*kikki=1 ikikk*kkiki=kkikk ikikk*kkikk=ikikikk ikikk*ikikik=ikikikki ikikk*ikikki=ikikkikikki ikikk*ikkiki=ikkikikki ikikk*ikkikk=ikkiki ikikk*kikikk=ikkikk ikikk*kikkik=k ikikk*kkikik=kki ikikk*ikikikk=ikikikkik ikikk*ikikkik=ikikkikikkik ikikk*ikkikik=ikkikikkik ikikk*kikikki=kikik ikikk*kikkiki=ki ikikk*kikkikk=kk ikikk*kkikikk=kkik ikikk*ikikikki=ikikikkiki ikikk*ikikkiki=kikkikikkik ikikk*ikikkikk=ikkikikkiki ikikk*ikkikikk=kikikkiki ikikk*kikikkik=kikikk ikikk*kikkikik=kik ikikk*kkikikki=kkiki ikikk*ikikikkik=kkikikki ikikk*ikikkikik=kkikikkiki ikikk*ikkikikki=kikikkik ikikk*kikikkiki=kikikki ikikk*kikikkikk=kiki ikikk*kikkikikk=kikk ikikk*kkikikkik=kkikik ikikk*ikikikkiki=kkikikk ikikk*ikikikkikk=kkikikkik ikikk*ikikkikikk=kikkikikki ikikk*ikkikikkik=kikikkikk ikikk*kikkikikki=kikki ikikk*kkikikkiki=kikkikk ikikk*ikikkikikki=kikkikikk ikikk*ikkikikkiki=kikkikik ikikk*kikkikikkik=kikkik ikikk*ikikkikikkik=kikkiki ikkik*1=ikkik ikkik*i=ikkiki ikkik*k=ikkikk ikkik*ik=ikkikik ikkik*ki=kikik ikkik*kk=ikki ikkik*iki=ikikkikk ikkik*ikk=ikkikikk ikkik*kik=kikikk ikkik*kki=ikk ikkik*ikik=ikikki ikkik*ikki=ikkikikki ikkik*kiki=kikikki ikkik*kikk=kiki ikkik*kkik=i ikkik*ikiki=ikikk ikkik*ikikk=ikikkik ikkik*ikkik=ikkikikkik ikkik*kikik=kikikkik ikkik*kikki=kik ikkik*kkiki=1 ikkik*kkikk=ik ikkik*ikikik=iki ikkik*ikikki=ikikkiki ikkik*ikkiki=ikkikikkiki ikkik*ikkikk=kikikkiki ikkik*kikikk=kikikkikk ikkik*kikkik=kikk ikkik*kkikik=k ikkik*ikikikk=ikik ikkik*ikikkik=ikikkikik ikkik*ikkikik=ikikkikikki ikkik*kikikki=kikkikik ikkik*kikkiki=kikki ikkik*kikkikk=ki ikkik*kkikikk=kk ikkik*ikikikki=ikiki ikkik*ikikkiki=ikikikkikk ikkik*ikikkikk=ikikkikikk ikkik*ikkikikk=ikikkikikkik ikkik*kikikkik=kikkikikk ikkik*kikkikik=kikkik ikkik*kkikikki=kki ikkik*ikikikkik=ikikik ikkik*ikikkikik=ikikikki ikkik*ikkikikki=kikkikikkik ikkik*kikikkiki=kikkikikki ikkik*kikikkikk=kikkiki ikkik*kikkikikk=kikkikk ikkik*kkikikkik=kkik ikkik*ikikikkiki=kkikk ikkik*ikikikkikk=ikikikk ikkik*ikikkikikk=ikikikkik ikkik*ikkikikkik=kkikikkiki ikkik*kikkikikki=kkikik ikkik*kkikikkiki=kkiki ikkik*ikikkikikki=ikikikkiki ikkik*ikkikikkiki=kkikikkik ikkik*kikkikikkik=kkikikk ikkik*ikikkikikkik=kkikikki kikik*1=kikik kikik*i=ikkikk kikik*k=kikikk kikik*ik=ikki kikik*ki=kikikki kikik*kk=kiki kikik*iki=ikk kikik*ikk=ikkik kikik*kik=kikikkik kikik*kki=kik kikik*ikik=i kikik*ikki=ikkiki kikik*kiki=kikikkiki kikik*kikk=kikikkikk kikik*kkik=kikk kikik*ikiki=1 kikik*ikikk=ik kikik*ikkik=ikkikik kikik*kikik=ikkikikki kikik*kikki=kikkikik kikik*kkiki=kikki kikik*kkikk=ki kikik*ikikik=k kikik*ikikki=iki kikik*ikkiki=ikikkikk kikik*ikkikk=ikkikikk kikik*kikikk=ikkikikkik kikik*kikkik=kikkikikk kikik*kkikik=kikkik kikik*ikikikk=kk kikik*ikikkik=ikik kikik*ikkikik=ikikki kikik*kikikki=ikkikikkiki kikik*kikkiki=kikkikikki kikik*kikkikk=kikkiki kikik*kkikikk=kikkikk kikik*ikikikki=kki kikik*ikikkiki=ikiki kikik*ikikkikk=ikikk kikik*ikkikikk=ikikkik kikik*kikikkik=ikikkikikki kikik*kikkikik=kikkikikkik kikik*kkikikki=kkikik kikik*ikikikkik=kkik kikik*ikikkikik=ikikik kikik*ikkikikki=ikikkiki kikik*kikikkiki=ikikkikikk kikik*kikikkikk=ikikkikikkik kikik*kikkikikk=kkikikkiki kikik*kkikikkik=kkikikk kikik*ikikikkiki=kkiki kikik*ikikikkikk=kkikk kikik*ikikkikikk=ikikikk kikik*ikkikikkik=ikikkikik kikik*kikkikikki=kkikikkik kikik*kkikikkiki=kkikikki kikik*ikikkikikki=ikikikki kikik*ikkikikkiki=ikikikkikk kikik*kikkikikkik=ikikikkiki kikik*ikikkikikkik=ikikikkik kikki*1=kikki kikki*i=kikk kikki*k=kikkik kikki*ik=ki kikki*ki=kikkiki kikki*kk=kikkikk kikki*iki=k kikki*ikk=kik kikki*kik=kikkikik kikki*kki=kkikik kikki*ikik=kk kikki*ikki=kiki kikki*kiki=kikikkikk kikki*kikk=kikkikikk kikki*kkik=kkikikk kikki*ikiki=kki kikki*ikikk=1 kikki*ikkik=kikik kikki*kikik=kikikki kikki*kikki=kikkikikki kikki*kkiki=kkikikki kikki*kkikk=kkiki kikki*ikikik=kkik kikki*ikikki=i kikki*ikkiki=ikkikk kikki*ikkikk=kikikk kikki*kikikk=kikikkik kikki*kikkik=kikkikikkik kikki*kkikik=kkikikkik kikki*ikikikk=kkikk kikki*ikikkik=ik kikki*ikkikik=ikki kikki*kikikki=kikikkiki kikki*kikkiki=ikikkikikkik kikki*kikkikk=kkikikkiki kikki*kkikikk=ikikikkiki kikki*ikikikki=ikikik kikki*ikikkiki=iki kikki*ikikkikk=ikk kikki*ikkikikk=ikkik kikki*kikikkik=ikkikikki kikki*kikkikik=ikkikikkiki kikki*kkikikki=ikikikkik kikki*ikikikkik=ikikikk kikki*ikikkikik=ikik kikki*ikkikikki=ikkiki kikki*kikikkiki=ikkikikk kikki*kikikkikk=ikkikikkik kikki*kikkikikk=ikikkikikki kikki*kkikikkik=ikikikkikk kikki*ikikikkiki=ikikikki kikki*ikikikkikk=ikiki kikki*ikikkikikk=ikikk kikki*ikkikikkik=ikkikik kikki*kikkikikki=ikikkikikk kikki*kkikikkiki=ikikkikik kikki*ikikkikikki=ikikki kikki*ikkikikkiki=ikikkikk kikki*kikkikikkik=ikikkiki kikki*ikikkikikkik=ikikkik kkiki*1=kkiki kkiki*i=kkik kkiki*k=kkikik kkiki*ik=kkikk kkiki*ki=kikkikk kkiki*kk=kkikikk kkiki*iki=ikikik kkiki*ikk=kki kkiki*kik=kikki kkiki*kki=kkikikki kkiki*ikik=ikikikk kkiki*ikki=kk kkiki*kiki=kikk kkiki*kikk=kikkik kkiki*kkik=kkikikkik kkiki*ikiki=ikikikki kkiki*ikikk=ikiki kkiki*ikkik=1 kkiki*kikik=ki kkiki*kikki=kikkiki kkiki*kkiki=kkikikkiki kkiki*kkikk=ikikikkiki kkiki*ikikik=ikikikkik kkiki*ikikki=ikik kkiki*ikkiki=i kkiki*ikkikk=k kkiki*kikikk=kik kkiki*kikkik=kikkikik kkiki*kkikik=kikkikikki kkiki*ikikikk=ikikikkikk kkiki*ikikkik=ikikk kkiki*ikkikik=ik kkiki*kikikki=kiki kkiki*kikkiki=kikikkikk kkiki*kikkikk=kikkikikk kkiki*kkikikk=kikkikikkik kkiki*ikikikki=ikikkikik kkiki*ikikkiki=ikikki kkiki*ikikkikk=iki kkiki*ikkikikk=ikk kkiki*kikikkik=kikik kkiki*kikkikik=kikikki kkiki*kkikikki=ikikkikikkik kkiki*ikikikkik=ikikkikikk kkiki*ikikkikik=ikikkik kkiki*ikkikikki=ikki kkiki*kikikkiki=ikkikk kkiki*kikikkikk=kikikk kkiki*kikkikikk=kikikkik kkiki*kkikikkik=ikkikikkiki kkiki*ikikikkiki=ikikkikikki kkiki*ikikikkikk=ikikkiki kkiki*ikikkikikk=ikikkikk kkiki*ikkikikkik=ikkik kkiki*kikkikikki=kikikkiki kkiki*kkikikkiki=ikkikikkik kkiki*ikikkikikki=ikkikik kkiki*ikkikikkiki=ikkiki kkiki*kikkikikkik=ikkikikki kkiki*ikikkikikkik=ikkikikk kkikk*1=kkikk kkikk*i=ikikik kkikk*k=kki kkikk*ik=ikikikk kkikk*ki=kk kkikk*kk=kkik kkikk*iki=ikikikki kkikk*ikk=ikiki kkikk*kik=1 kkikk*kki=kkiki kkikk*ikik=ikikikkik kkikk*ikki=ikik kkikk*kiki=i kkikk*kikk=k kkikk*kkik=kkikik kkikk*ikiki=ikikikkiki kkikk*ikikk=ikikikkikk kkikk*ikkik=ikikk kkikk*kikik=ik kkikk*kikki=ki kkikk*kkiki=kikkikk kkikk*kkikk=kkikikk kkikk*ikikik=kkikikki kkikk*ikikki=ikikkikik kkikk*ikkiki=ikikki kkikk*ikkikk=iki kkikk*kikikk=ikk kkikk*kikkik=kik kkikk*kkikik=kikki kkikk*ikikikk=kkikikkik kkikk*ikikkik=ikikkikikk kkikk*ikkikik=ikikkik kkikk*kikikki=ikki kkikk*kikkiki=kiki kkikk*kikkikk=kikk kkikk*kkikikk=kikkik kkikk*ikikikki=kkikikkiki kkikk*ikikkiki=ikikkikikki kkikk*ikikkikk=ikikkiki kkikk*ikkikikk=ikikkikk kkikk*kikikkik=ikkik kkikk*kikkikik=kikik kkikk*kkikikki=kikkiki kkikk*ikikikkik=kikkikikki kkikk*ikikkikik=ikikkikikkik kkikk*ikkikikki=ikkikik kkikk*kikikkiki=ikkiki kkikk*kikikkikk=ikkikk kkikk*kikkikikk=kikikk kkikk*kkikikkik=kikkikik kkikk*ikikikkiki=kikkikikk kkikk*ikikikkikk=kikkikikkik kkikk*ikikkikikk=ikkikikkiki kkikk*ikkikikkik=ikkikikk kkikk*kikkikikki=kikikki kkikk*kkikikkiki=kikikkikk kkikk*ikikkikikki=ikkikikkik kkikk*ikkikikkiki=ikkikikki kkikk*kikkikikkik=kikikkik kkikk*ikikkikikkik=kikikkiki ikikik*1=ikikik ikikik*i=kkikk ikikik*k=ikikikk ikikik*ik=kki ikikik*ki=ikikikki ikikik*kk=ikiki ikikik*iki=kk ikikik*ikk=kkik ikikik*kik=ikikikkik ikikik*kki=ikik ikikik*ikik=1 ikikik*ikki=kkiki ikikik*kiki=ikikikkiki ikikik*kikk=ikikikkikk ikikik*kkik=ikikk ikikik*ikiki=i ikikik*ikikk=k ikikik*ikkik=kkikik ikikik*kikik=kkikikki ikikik*kikki=ikikkikik ikikik*kkiki=ikikki ikikik*kkikk=iki ikikik*ikikik=ik ikikik*ikikki=ki ikikik*ikkiki=kikkikk ikikik*ikkikk=kkikikk ikikik*kikikk=kkikikkik ikikik*kikkik=ikikkikikk ikikik*kkikik=ikikkik ikikik*ikikikk=ikk ikikik*ikikkik=kik ikikik*ikkikik=kikki ikikik*kikikki=kkikikkiki ikikik*kikkiki=ikikkikikki ikikik*kikkikk=ikikkiki ikikik*kkikikk=ikikkikk ikikik*ikikikki=ikki ikikik*ikikkiki=kiki ikikik*ikikkikk=kikk ikikik*ikkikikk=kikkik ikikik*kikikkik=kikkikikki ikikik*kikkikik=ikikkikikkik ikikik*kkikikki=ikkikik ikikik*ikikikkik=ikkik ikikik*ikikkikik=kikik ikikik*ikkikikki=kikkiki ikikik*kikikkiki=kikkikikk ikikik*kikikkikk=kikkikikkik ikikik*kikkikikk=ikkikikkiki ikikik*kkikikkik=ikkikikk ikikik*ikikikkiki=ikkiki ikikik*ikikikkikk=ikkikk ikikik*ikikkikikk=kikikk ikikik*ikkikikkik=kikkikik ikikik*kikkikikki=ikkikikkik ikikik*kkikikkiki=ikkikikki ikikik*ikikkikikki=kikikki ikikik*ikkikikkiki=kikikkikk ikikik*kikkikikkik=kikikkiki ikikik*ikikkikikkik=kikikkik ikikki*1=ikikki ikikki*i=ikikk ikikki*k=ikikkik ikikki*ik=iki ikikki*ki=ikikkiki ikikki*kk=ikikkikk ikikki*iki=ik ikikki*ikk=ikik ikikki*kik=ikikkikik ikikki*kki=ikkikik ikikki*ikik=ikk ikikki*ikki=ikiki ikikki*kiki=ikikikkikk ikikki*kikk=ikikkikikk ikikki*kkik=ikkikikk ikikki*ikiki=ikki ikikki*ikikk=i ikikki*ikkik=ikikik ikikki*kikik=ikikikki ikikki*kikki=ikikkikikki ikikki*kkiki=ikkikikki ikikki*kkikk=ikkiki ikikki*ikikik=ikkik ikikki*ikikki=1 ikikki*ikkiki=kkikk ikikki*ikkikk=ikikikk ikikki*kikikk=ikikikkik ikikki*kikkik=ikikkikikkik ikikki*kkikik=ikkikikkik ikikki*ikikikk=ikkikk ikikki*ikikkik=k ikikki*ikkikik=kki ikikki*kikikki=ikikikkiki ikikki*kikkiki=kikkikikkik ikikki*kikkikk=ikkikikkiki ikikki*kkikikk=kikikkiki ikikki*ikikikki=kikik ikikki*ikikkiki=ki ikikki*ikikkikk=kk ikikki*ikkikikk=kkik ikikki*kikikkik=kkikikki ikikki*kikkikik=kkikikkiki ikikki*kkikikki=kikikkik ikikki*ikikikkik=kikikk ikikki*ikikkikik=kik ikikki*ikkikikki=kkiki ikikki*kikikkiki=kkikikk ikikki*kikikkikk=kkikikkik ikikki*kikkikikk=kikkikikki ikikki*kkikikkik=kikikkikk ikikki*ikikikkiki=kikikki ikikki*ikikikkikk=kiki ikikki*ikikkikikk=kikk ikikki*ikkikikkik=kkikik ikikki*kikkikikki=kikkikikk ikikki*kkikikkiki=kikkikik ikikki*ikikkikikki=kikki ikikki*ikkikikkiki=kikkikk ikikki*kikkikikkik=kikkiki ikikki*ikikkikikkik=kikkik ikkiki*1=ikkiki ikkiki*i=ikkik ikkiki*k=ikkikik ikkiki*ik=ikkikk ikkiki*ki=ikikkikk ikkiki*kk=ikkikikk ikkiki*iki=kikik ikkiki*ikk=ikki ikkiki*kik=ikikki ikkiki*kki=ikkikikki ikkiki*ikik=kikikk ikkiki*ikki=ikk ikkiki*kiki=ikikk ikkiki*kikk=ikikkik ikkiki*kkik=ikkikikkik ikkiki*ikiki=kikikki ikkiki*ikikk=kiki ikkiki*ikkik=i ikkiki*kikik=iki ikkiki*kikki=ikikkiki ikkiki*kkiki=ikkikikkiki ikkiki*kkikk=kikikkiki ikkiki*ikikik=kikikkik ikkiki*ikikki=kik ikkiki*ikkiki=1 ikkiki*ikkikk=ik ikkiki*kikikk=ikik ikkiki*kikkik=ikikkikik ikkiki*kkikik=ikikkikikki ikkiki*ikikikk=kikikkikk ikkiki*ikikkik=kikk ikkiki*ikkikik=k ikkiki*kikikki=ikiki ikkiki*kikkiki=ikikikkikk ikkiki*kikkikk=ikikkikikk ikkiki*kkikikk=ikikkikikkik ikkiki*ikikikki=kikkikik ikkiki*ikikkiki=kikki ikkiki*ikikkikk=ki ikkiki*ikkikikk=kk ikkiki*kikikkik=ikikik ikkiki*kikkikik=ikikikki ikkiki*kkikikki=kikkikikkik ikkiki*ikikikkik=kikkikikk ikkiki*ikikkikik=kikkik ikkiki*ikkikikki=kki ikkiki*kikikkiki=kkikk ikkiki*kikikkikk=ikikikk ikkiki*kikkikikk=ikikikkik ikkiki*kkikikkik=kkikikkiki ikkiki*ikikikkiki=kikkikikki ikkiki*ikikikkikk=kikkiki ikkiki*ikikkikikk=kikkikk ikkiki*ikkikikkik=kkik ikkiki*kikkikikki=ikikikkiki ikkiki*kkikikkiki=kkikikkik ikkiki*ikikkikikki=kkikik ikkiki*ikkikikkiki=kkiki ikkiki*kikkikikkik=kkikikki ikkiki*ikikkikikkik=kkikikk ikkikk*1=ikkikk ikkikk*i=kikik ikkikk*k=ikki ikkikk*ik=kikikk ikkikk*ki=ikk ikkikk*kk=ikkik ikkikk*iki=kikikki ikkikk*ikk=kiki ikkikk*kik=i ikkikk*kki=ikkiki ikkikk*ikik=kikikkik ikkikk*ikki=kik ikkikk*kiki=1 ikkikk*kikk=ik ikkikk*kkik=ikkikik ikkikk*ikiki=kikikkiki ikkikk*ikikk=kikikkikk ikkikk*ikkik=kikk ikkikk*kikik=k ikkikk*kikki=iki ikkikk*kkiki=ikikkikk ikkikk*kkikk=ikkikikk ikkikk*ikikik=ikkikikki ikkikk*ikikki=kikkikik ikkikk*ikkiki=kikki ikkikk*ikkikk=ki ikkikk*kikikk=kk ikkikk*kikkik=ikik ikkikk*kkikik=ikikki ikkikk*ikikikk=ikkikikkik ikkikk*ikikkik=kikkikikk ikkikk*ikkikik=kikkik ikkikk*kikikki=kki ikkikk*kikkiki=ikiki ikkikk*kikkikk=ikikk ikkikk*kkikikk=ikikkik ikkikk*ikikikki=ikkikikkiki ikkikk*ikikkiki=kikkikikki ikkikk*ikikkikk=kikkiki ikkikk*ikkikikk=kikkikk ikkikk*kikikkik=kkik ikkikk*kikkikik=ikikik ikkikk*kkikikki=ikikkiki ikkikk*ikikikkik=ikikkikikki ikkikk*ikikkikik=kikkikikkik ikkikk*ikkikikki=kkikik ikkikk*kikikkiki=kkiki ikkikk*kikikkikk=kkikk ikkikk*kikkikikk=ikikikk ikkikk*kkikikkik=ikikkikik ikkikk*ikikikkiki=ikikkikikk ikkikk*ikikikkikk=ikikkikikkik ikkikk*ikikkikikk=kkikikkiki ikkikk*ikkikikkik=kkikikk ikkikk*kikkikikki=ikikikki ikkikk*kkikikkiki=ikikikkikk ikkikk*ikikkikikki=kkikikkik ikkikk*ikkikikkiki=kkikikki ikkikk*kikkikikkik=ikikikkik ikkikk*ikikkikikkik=ikikikkiki kikikk*1=kikikk kikikk*i=kikikki kikikk*k=kiki kikikk*ik=kikikkik kikikk*ki=kik kikikk*kk=kikik kikikk*iki=kikikkiki kikikk*ikk=kikikkikk kikikk*kik=kikk kikikk*kki=ikkikk kikikk*ikik=ikkikikki kikikk*ikki=kikkikik kikikk*kiki=kikki kikikk*kikk=ki kikikk*kkik=ikki kikikk*ikiki=ikkikikk kikikk*ikikk=ikkikikkik kikikk*ikkik=kikkikikk kikikk*kikik=kikkik kikikk*kikki=k kikikk*kkiki=ikk kikikk*kkikk=ikkik kikikk*ikikik=ikkiki kikikk*ikikki=ikkikikkiki kikikk*ikkiki=kikkikikki kikikk*ikkikk=kikkiki kikikk*kikikk=kikkikk kikikk*kikkik=kk kikikk*kkikik=i kikikk*ikikikk=ikkikik kikikk*ikikkik=ikikkikikki kikikk*ikkikik=kikkikikkik kikikk*kikikki=kkikik kikikk*kikkiki=kki kikikk*kikkikk=1 kikikk*kkikikk=ik kikikk*ikikikki=ikikkikk kikikk*ikikkiki=ikikkikikk kikikk*ikikkikk=ikikkikikkik kikikk*ikkikikk=kkikikkiki kikikk*kikikkik=kkikikk kikikk*kikkikik=kkik kikikk*kkikikki=iki kikikk*ikikikkik=ikikki kikikk*ikikkikik=ikikkiki kikikk*ikkikikki=kkikikkik kikikk*kikikkiki=kkikikki kikikk*kikikkikk=kkiki kikikk*kikkikikk=kkikk kikikk*kkikikkik=ikik kikikk*ikikikkiki=ikikk kikikk*ikikikkikk=ikikkik kikikk*ikikkikikk=ikikkikik kikikk*ikkikikkik=ikikikkiki kikikk*kikkikikki=ikikik kikikk*kkikikkiki=ikiki kikikk*ikikkikikki=ikikikkikk kikikk*ikkikikkiki=ikikikkik kikikk*kikkikikkik=ikikikk kikikk*ikikkikikkik=ikikikki kikkik*1=kikkik kikkik*i=kikkiki kikkik*k=kikkikk kikkik*ik=kikkikik kikkik*ki=kkikik kikkik*kk=kikki kikkik*iki=kikikkikk kikkik*ikk=kikkikikk kikkik*kik=kkikikk kikkik*kki=kikk kikkik*ikik=kikikki kikkik*ikki=kikkikikki kikkik*kiki=kkikikki kikkik*kikk=kkiki kikkik*kkik=ki kikkik*ikiki=kikikk kikkik*ikikk=kikikkik kikkik*ikkik=kikkikikkik kikkik*kikik=kkikikkik kikkik*kikki=kkik kikkik*kkiki=k kikkik*kkikk=kik kikkik*ikikik=kiki kikkik*ikikki=kikikkiki kikkik*ikkiki=ikikkikikkik kikkik*ikkikk=kkikikkiki kikkik*kikikk=ikikikkiki kikkik*kikkik=kkikk kikkik*kkikik=kk kikkik*ikikikk=kikik kikkik*ikikkik=ikkikikki kikkik*ikkikik=ikkikikkiki kikkik*kikikki=ikikikkik kikkik*kikkiki=ikikik kikkik*kikkikk=kki kikkik*kkikikk=1 kikkik*ikikikki=ikkikk kikkik*ikikkiki=ikkikikk kikkik*ikikkikk=ikkikikkik kikkik*ikkikikk=ikikkikikki kikkik*kikikkik=ikikikkikk kikkik*kikkikik=ikikikk kikkik*kkikikki=i kikkik*ikikikkik=ikki kikkik*ikikkikik=ikkiki kikkik*ikkikikki=ikikkikikk kikkik*kikikkiki=ikikkikik kikkik*kikikkikk=ikikikki kikkik*kikkikikk=ikiki kikkik*kkikikkik=ik kikkik*ikikikkiki=ikk kikkik*ikikikkikk=ikkik kikkik*ikikkikikk=ikkikik kikkik*ikkikikkik=ikikkiki kikkik*kikkikikki=ikik kikkik*kkikikkiki=iki kikkik*ikikkikikki=ikikkikk kikkik*ikkikikkiki=ikikkik kikkik*kikkikikkik=ikikk kikkik*ikikkikikkik=ikikki kkikik*1=kkikik kkikik*i=kikkikk kkikik*k=kkikikk kkikik*ik=kikki kkikik*ki=kkikikki kkikik*kk=kkiki kkikik*iki=kikk kkikik*ikk=kikkik kkikik*kik=kkikikkik kkikik*kki=kkik kkikik*ikik=ki kkikik*ikki=kikkiki kkikik*kiki=kkikikkiki kkikik*kikk=ikikikkiki kkikik*kkik=kkikk kkikik*ikiki=k kkikik*ikikk=kik kkikik*ikkik=kikkikik kkikik*kikik=kikkikikki kkikik*kikki=ikikikkik kkikik*kkiki=ikikik kkikik*kkikk=kki kkikik*ikikik=kk kkikik*ikikki=kiki kkikik*ikkiki=kikikkikk kkikik*ikkikk=kikkikikk kkikik*kikikk=kikkikikkik kkikik*kikkik=ikikikkikk kkikik*kkikik=ikikikk kkikik*ikikikk=1 kkikik*ikikkik=kikik kkikik*ikkikik=kikikki kkikik*kikikki=ikikkikikkik kkikik*kikkiki=ikikkikik kkikik*kikkikk=ikikikki kkikik*kkikikk=ikiki kkikik*ikikikki=i kkikik*ikikkiki=ikkikk kkikik*ikikkikk=kikikk kkikik*ikkikikk=kikikkik kkikik*kikikkik=ikkikikkiki kkikik*kikkikik=ikikkikikk kkikik*kkikikki=ikik kkikik*ikikikkik=ik kkikik*ikikkikik=ikki kkikik*ikkikikki=kikikkiki kkikik*kikikkiki=ikkikikkik kkikik*kikikkikk=ikikkikikki kkikik*kikkikikk=ikikkiki kkikik*kkikikkik=ikikk kkikik*ikikikkiki=iki kkikik*ikikikkikk=ikk kkikik*ikikkikikk=ikkik kkikik*ikkikikkik=ikkikikki kkikik*kikkikikki=ikikkik kkikik*kkikikkiki=ikikki kkikik*ikikkikikki=ikkiki kkikik*ikkikikkiki=ikkikikk kkikik*kikkikikkik=ikikkikk kkikik*ikikkikikkik=ikkikik ikikikk*1=ikikikk ikikikk*i=ikikikki ikikikk*k=ikiki ikikikk*ik=ikikikkik ikikikk*ki=ikik ikikikk*kk=ikikik ikikikk*iki=ikikikkiki ikikikk*ikk=ikikikkikk ikikikk*kik=ikikk ikikikk*kki=kkikk ikikikk*ikik=kkikikki ikikikk*ikki=ikikkikik ikikikk*kiki=ikikki ikikikk*kikk=iki ikikikk*kkik=kki ikikikk*ikiki=kkikikk ikikikk*ikikk=kkikikkik ikikikk*ikkik=ikikkikikk ikikikk*kikik=ikikkik ikikikk*kikki=ik ikikikk*kkiki=kk ikikikk*kkikk=kkik ikikikk*ikikik=kkiki ikikikk*ikikki=kkikikkiki ikikikk*ikkiki=ikikkikikki ikikikk*ikkikk=ikikkiki ikikikk*kikikk=ikikkikk ikikikk*kikkik=ikk ikikikk*kkikik=1 ikikikk*ikikikk=kkikik ikikikk*ikikkik=kikkikikki ikikikk*ikkikik=ikikkikikkik ikikikk*kikikki=ikkikik ikikikk*kikkiki=ikki ikikikk*kikkikk=i ikikikk*kkikikk=k ikikikk*ikikikki=kikkikk ikikikk*ikikkiki=kikkikikk ikikikk*ikikkikk=kikkikikkik ikikikk*ikkikikk=ikkikikkiki ikikikk*kikikkik=ikkikikk ikikikk*kikkikik=ikkik ikikikk*kkikikki=ki ikikikk*ikikikkik=kikki ikikikk*ikikkikik=kikkiki ikikikk*ikkikikki=ikkikikkik ikikikk*kikikkiki=ikkikikki ikikikk*kikikkikk=ikkiki ikikikk*kikkikikk=ikkikk ikikikk*kkikikkik=kik ikikikk*ikikikkiki=kikk ikikikk*ikikikkikk=kikkik ikikikk*ikikkikikk=kikkikik ikikikk*ikkikikkik=kikikkiki ikikikk*kikkikikki=kikik ikikikk*kkikikkiki=kiki ikikikk*ikikkikikki=kikikkikk ikikikk*ikkikikkiki=kikikkik ikikikk*kikkikikkik=kikikk ikikikk*ikikkikikkik=kikikki ikikkik*1=ikikkik ikikkik*i=ikikkiki ikikkik*k=ikikkikk ikikkik*ik=ikikkikik ikikkik*ki=ikkikik ikikkik*kk=ikikki ikikkik*iki=ikikikkikk ikikkik*ikk=ikikkikikk ikikkik*kik=ikkikikk ikikkik*kki=ikikk ikikkik*ikik=ikikikki ikikkik*ikki=ikikkikikki ikikkik*kiki=ikkikikki ikikkik*kikk=ikkiki ikikkik*kkik=iki ikikkik*ikiki=ikikikk ikikkik*ikikk=ikikikkik ikikkik*ikkik=ikikkikikkik ikikkik*kikik=ikkikikkik ikikkik*kikki=ikkik ikikkik*kkiki=ik ikikkik*kkikk=ikik ikikkik*ikikik=ikiki ikikkik*ikikki=ikikikkiki ikikkik*ikkiki=kikkikikkik ikikkik*ikkikk=ikkikikkiki ikikkik*kikikk=kikikkiki ikikkik*kikkik=ikkikk ikikkik*kkikik=ikk ikikkik*ikikikk=ikikik ikikkik*ikikkik=kkikikki ikikkik*ikkikik=kkikikkiki ikikkik*kikikki=kikikkik ikikkik*kikkiki=kikik ikikkik*kikkikk=ikki ikikkik*kkikikk=i ikikkik*ikikikki=kkikk ikikkik*ikikkiki=kkikikk ikikkik*ikikkikk=kkikikkik ikikkik*ikkikikk=kikkikikki ikikkik*kikikkik=kikikkikk ikikkik*kikkikik=kikikk ikikkik*kkikikki=1 ikikkik*ikikikkik=kki ikikkik*ikikkikik=kkiki ikikkik*ikkikikki=kikkikikk ikikkik*kikikkiki=kikkikik ikikkik*kikikkikk=kikikki ikikkik*kikkikikk=kiki ikikkik*kkikikkik=k ikikkik*ikikikkiki=kk ikikkik*ikikikkikk=kkik ikikkik*ikikkikikk=kkikik ikikkik*ikkikikkik=kikkiki ikikkik*kikkikikki=kik ikikkik*kkikikkiki=ki ikikkik*ikikkikikki=kikkikk ikikkik*ikkikikkiki=kikkik ikikkik*kikkikikkik=kikk ikikkik*ikikkikikkik=kikki ikkikik*1=ikkikik ikkikik*i=ikikkikk ikkikik*k=ikkikikk ikkikik*ik=ikikki ikkikik*ki=ikkikikki ikkikik*kk=ikkiki ikkikik*iki=ikikk ikkikik*ikk=ikikkik ikkikik*kik=ikkikikkik ikkikik*kki=ikkik ikkikik*ikik=iki ikkikik*ikki=ikikkiki ikkikik*kiki=ikkikikkiki ikkikik*kikk=kikikkiki ikkikik*kkik=ikkikk ikkikik*ikiki=ik ikkikik*ikikk=ikik ikkikik*ikkik=ikikkikik ikkikik*kikik=ikikkikikki ikkikik*kikki=kikikkik ikkikik*kkiki=kikik ikkikik*kkikk=ikki ikkikik*ikikik=ikk ikkikik*ikikki=ikiki ikkikik*ikkiki=ikikikkikk ikkikik*ikkikk=ikikkikikk ikkikik*kikikk=ikikkikikkik ikkikik*kikkik=kikikkikk ikkikik*kkikik=kikikk ikkikik*ikikikk=i ikkikik*ikikkik=ikikik ikkikik*ikkikik=ikikikki ikkikik*kikikki=kikkikikkik ikkikik*kikkiki=kikkikik ikkikik*kikkikk=kikikki ikkikik*kkikikk=kiki ikkikik*ikikikki=1 ikkikik*ikikkiki=kkikk ikkikik*ikikkikk=ikikikk ikkikik*ikkikikk=ikikikkik ikkikik*kikikkik=kkikikkiki ikkikik*kikkikik=kikkikikk ikkikik*kkikikki=kik ikkikik*ikikikkik=k ikkikik*ikikkikik=kki ikkikik*ikkikikki=ikikikkiki ikkikik*kikikkiki=kkikikkik ikkikik*kikikkikk=kikkikikki ikkikik*kikkikikk=kikkiki ikkikik*kkikikkik=kikk ikkikik*ikikikkiki=ki ikkikik*ikikikkikk=kk ikkikik*ikikkikikk=kkik ikkikik*ikkikikkik=kkikikki ikkikik*kikkikikki=kikkik ikkikik*kkikikkiki=kikki ikkikik*ikikkikikki=kkiki ikkikik*ikkikikkiki=kkikikk ikkikik*kikkikikkik=kikkikk ikkikik*ikikkikikkik=kkikik kikikki*1=kikikki kikikki*i=kikikk kikikki*k=kikikkik kikikki*ik=kiki kikikki*ki=kikikkiki kikikki*kk=kikikkikk kikikki*iki=kik kikikki*ikk=kikik kikikki*kik=ikkikikki kikikki*kki=kikkikik kikikki*ikik=kikk kikikki*ikki=ikkikk kikikki*kiki=ikkikikk kikikki*kikk=ikkikikkik kikikki*kkik=kikkikikk kikikki*ikiki=kikki kikikki*ikikk=ki kikikki*ikkik=ikki kikikki*kikik=ikkiki kikikki*kikki=ikkikikkiki kikikki*kkiki=kikkikikki kikikki*kkikk=kikkiki kikikki*ikikik=kikkik kikikki*ikikki=k kikikki*ikkiki=ikk kikikki*ikkikk=ikkik kikikki*kikikk=ikkikik kikikki*kikkik=ikikkikikki kikikki*kkikik=kikkikikkik kikikki*ikikikk=kikkikk kikikki*ikikkik=kk kikikki*ikkikik=i kikikki*kikikki=ikikkikk kikikki*kikkiki=ikikkikikk kikikki*kikkikk=ikikkikikkik kikikki*kkikikk=kkikikkiki kikikki*ikikikki=kkikik kikikki*ikikkiki=kki kikikki*ikikkikk=1 kikikki*ikkikikk=ik kikikki*kikikkik=ikikki kikikki*kikkikik=ikikkiki kikikki*kkikikki=kkikikkik kikikki*ikikikkik=kkikikk kikikki*ikikkikik=kkik kikikki*ikkikikki=iki kikikki*kikikkiki=ikikk kikikki*kikikkikk=ikikkik kikikki*kikkikikk=ikikkikik kikikki*kkikikkik=ikikikkiki kikikki*ikikikkiki=kkikikki kikikki*ikikikkikk=kkiki kikikki*ikikkikikk=kkikk kikikki*ikkikikkik=ikik kikikki*kikkikikki=ikikikkikk kikikki*kkikikkiki=ikikikkik kikikki*ikikkikikki=ikikik kikikki*ikkikikkiki=ikiki kikikki*kikkikikkik=ikikikki kikikki*ikikkikikkik=ikikikk kikkiki*1=kikkiki kikkiki*i=kikkik kikkiki*k=kikkikik kikkiki*ik=kikkikk kikkiki*ki=kikikkikk kikkiki*kk=kikkikikk kikkiki*iki=kkikik kikkiki*ikk=kikki kikkiki*kik=kikikki kikkiki*kki=kikkikikki kikkiki*ikik=kkikikk kikkiki*ikki=kikk kikkiki*kiki=kikikk kikkiki*kikk=kikikkik kikkiki*kkik=kikkikikkik kikkiki*ikiki=kkikikki kikkiki*ikikk=kkiki kikkiki*ikkik=ki kikkiki*kikik=kiki kikkiki*kikki=kikikkiki kikkiki*kkiki=ikikkikikkik kikkiki*kkikk=kkikikkiki kikkiki*ikikik=kkikikkik kikkiki*ikikki=kkik kikkiki*ikkiki=k kikkiki*ikkikk=kik kikkiki*kikikk=kikik kikkiki*kikkik=ikkikikki kikkiki*kkikik=ikkikikkiki kikkiki*ikikikk=ikikikkiki kikkiki*ikikkik=kkikk kikkiki*ikkikik=kk kikkiki*kikikki=ikkikk kikkiki*kikkiki=ikkikikk kikkiki*kikkikk=ikkikikkik kikkiki*kkikikk=ikikkikikki kikkiki*ikikikki=ikikikkik kikkiki*ikikkiki=ikikik kikkiki*ikikkikk=kki kikkiki*ikkikikk=1 kikkiki*kikikkik=ikki kikkiki*kikkikik=ikkiki kikkiki*kkikikki=ikikkikikk kikkiki*ikikikkik=ikikikkikk kikkiki*ikikkikik=ikikikk kikkiki*ikkikikki=i kikkiki*kikikkiki=ikk kikkiki*kikikkikk=ikkik kikkiki*kikkikikk=ikkikik kikkiki*kkikikkik=ikikkiki kikkiki*ikikikkiki=ikikkikik kikkiki*ikikikkikk=ikikikki kikkiki*ikikkikikk=ikiki kikkiki*ikkikikkik=ik kikkiki*kikkikikki=ikikkikk kikkiki*kkikikkiki=ikikkik kikkiki*ikikkikikki=ikik kikkiki*ikkikikkiki=iki kikkiki*kikkikikkik=ikikki kikkiki*ikikkikikkik=ikikk kikkikk*1=kikkikk kikkikk*i=kkikik kikkikk*k=kikki kikkikk*ik=kkikikk kikkikk*ki=kikk kikkikk*kk=kikkik kikkikk*iki=kkikikki kikkikk*ikk=kkiki kikkikk*kik=ki kikkikk*kki=kikkiki kikkikk*ikik=kkikikkik kikkikk*ikki=kkik kikkikk*kiki=k kikkikk*kikk=kik kikkikk*kkik=kikkikik kikkikk*ikiki=kkikikkiki kikkikk*ikikk=ikikikkiki kikkikk*ikkik=kkikk kikkikk*kikik=kk kikkikk*kikki=kiki kikkikk*kkiki=kikikkikk kikkikk*kkikk=kikkikikk kikkikk*ikikik=kikkikikki kikkikk*ikikki=ikikikkik kikkikk*ikkiki=ikikik kikkikk*ikkikk=kki kikkikk*kikikk=1 kikkikk*kikkik=kikik kikkikk*kkikik=kikikki kikkikk*ikikikk=kikkikikkik kikkikk*ikikkik=ikikikkikk kikkikk*ikkikik=ikikikk kikkikk*kikikki=i kikkikk*kikkiki=ikkikk kikkikk*kikkikk=kikikk kikkikk*kkikikk=kikikkik kikkikk*ikikikki=ikikkikikkik kikkikk*ikikkiki=ikikkikik kikkikk*ikikkikk=ikikikki kikkikk*ikkikikk=ikiki kikkikk*kikikkik=ik kikkikk*kikkikik=ikki kikkikk*kkikikki=kikikkiki kikkikk*ikikikkik=ikkikikkiki kikkikk*ikikkikik=ikikkikikk kikkikk*ikkikikki=ikik kikkikk*kikikkiki=iki kikkikk*kikikkikk=ikk kikkikk*kikkikikk=ikkik kikkikk*kkikikkik=ikkikikki kikkikk*ikikikkiki=ikkikikkik kikkikk*ikikikkikk=ikikkikikki kikkikk*ikikkikikk=ikikkiki kikkikk*ikkikikkik=ikikk kikkikk*kikkikikki=ikkiki kikkikk*kkikikkiki=ikkikikk kikkikk*ikikkikikki=ikikkik kikkikk*ikkikikkiki=ikikki kikkikk*kikkikikkik=ikkikik kikkikk*ikikkikikkik=ikikkikk kkikikk*1=kkikikk kkikikk*i=kkikikki kkikikk*k=kkiki kkikikk*ik=kkikikkik kkikikk*ki=kkik kkikikk*kk=kkikik kkikikk*iki=kkikikkiki kkikikk*ikk=ikikikkiki kkikikk*kik=kkikk kkikikk*kki=kikkikk kkikikk*ikik=kikkikikki kkikikk*ikki=ikikikkik kkikikk*kiki=ikikik kkikikk*kikk=kki kkikikk*kkik=kikki kkikikk*ikiki=kikkikikk kkikikk*ikikk=kikkikikkik kkikikk*ikkik=ikikikkikk kkikikk*kikik=ikikikk kkikikk*kikki=kk kkikikk*kkiki=kikk kkikikk*kkikk=kikkik kkikikk*ikikik=kikkiki kkikikk*ikikki=ikikkikikkik kkikikk*ikkiki=ikikkikik kkikikk*ikkikk=ikikikki kkikikk*kikikk=ikiki kkikikk*kikkik=1 kkikikk*kkikik=ki kkikikk*ikikikk=kikkikik kkikikk*ikikkik=ikkikikkiki kkikikk*ikkikik=ikikkikikk kkikikk*kikikki=ikik kkikikk*kikkiki=i kkikikk*kikkikk=k kkikikk*kkikikk=kik kkikikk*ikikikki=kikikkikk kkikikk*ikikkiki=ikkikikkik kkikikk*ikikkikk=ikikkikikki kkikikk*ikkikikk=ikikkiki kkikikk*kikikkik=ikikk kkikikk*kikkikik=ik kkikikk*kkikikki=kiki kkikikk*ikikikkik=kikikki kkikikk*ikikkikik=kikikkiki kkikikk*ikkikikki=ikikkik kkikikk*kikikkiki=ikikki kkikikk*kikikkikk=iki kkikikk*kikkikikk=ikk kkikikk*kkikikkik=kikik kkikikk*ikikikkiki=kikikk kkikikk*ikikikkikk=kikikkik kkikikk*ikikkikikk=ikkikikki kkikikk*ikkikikkik=ikikkikk kkikikk*kikkikikki=ikki kkikikk*kkikikkiki=ikkikk kkikikk*ikikkikikki=ikkikikk kkikikk*ikkikikkiki=ikkikik kkikikk*kikkikikkik=ikkik kkikikk*ikikkikikkik=ikkiki ikikikki*1=ikikikki ikikikki*i=ikikikk ikikikki*k=ikikikkik ikikikki*ik=ikiki ikikikki*ki=ikikikkiki ikikikki*kk=ikikikkikk ikikikki*iki=ikik ikikikki*ikk=ikikik ikikikki*kik=kkikikki ikikikki*kki=ikikkikik ikikikki*ikik=ikikk ikikikki*ikki=kkikk ikikikki*kiki=kkikikk ikikikki*kikk=kkikikkik ikikikki*kkik=ikikkikikk ikikikki*ikiki=ikikki ikikikki*ikikk=iki ikikikki*ikkik=kki ikikikki*kikik=kkiki ikikikki*kikki=kkikikkiki ikikikki*kkiki=ikikkikikki ikikikki*kkikk=ikikkiki ikikikki*ikikik=ikikkik ikikikki*ikikki=ik ikikikki*ikkiki=kk ikikikki*ikkikk=kkik ikikikki*kikikk=kkikik ikikikki*kikkik=kikkikikki ikikikki*kkikik=ikikkikikkik ikikikki*ikikikk=ikikkikk ikikikki*ikikkik=ikk ikikikki*ikkikik=1 ikikikki*kikikki=kikkikk ikikikki*kikkiki=kikkikikk ikikikki*kikkikk=kikkikikkik ikikikki*kkikikk=ikkikikkiki ikikikki*ikikikki=ikkikik ikikikki*ikikkiki=ikki ikikikki*ikikkikk=i ikikikki*ikkikikk=k ikikikki*kikikkik=kikki ikikikki*kikkikik=kikkiki ikikikki*kkikikki=ikkikikkik ikikikki*ikikikkik=ikkikikk ikikikki*ikikkikik=ikkik ikikikki*ikkikikki=ki ikikikki*kikikkiki=kikk ikikikki*kikikkikk=kikkik ikikikki*kikkikikk=kikkikik ikikikki*kkikikkik=kikikkiki ikikikki*ikikikkiki=ikkikikki ikikikki*ikikikkikk=ikkiki ikikikki*ikikkikikk=ikkikk ikikikki*ikkikikkik=kik ikikikki*kikkikikki=kikikkikk ikikikki*kkikikkiki=kikikkik ikikikki*ikikkikikki=kikik ikikikki*ikkikikkiki=kiki ikikikki*kikkikikkik=kikikki ikikikki*ikikkikikkik=kikikk ikikkiki*1=ikikkiki ikikkiki*i=ikikkik ikikkiki*k=ikikkikik ikikkiki*ik=ikikkikk ikikkiki*ki=ikikikkikk ikikkiki*kk=ikikkikikk ikikkiki*iki=ikkikik ikikkiki*ikk=ikikki ikikkiki*kik=ikikikki ikikkiki*kki=ikikkikikki ikikkiki*ikik=ikkikikk ikikkiki*ikki=ikikk ikikkiki*kiki=ikikikk ikikkiki*kikk=ikikikkik ikikkiki*kkik=ikikkikikkik ikikkiki*ikiki=ikkikikki ikikkiki*ikikk=ikkiki ikikkiki*ikkik=iki ikikkiki*kikik=ikiki ikikkiki*kikki=ikikikkiki ikikkiki*kkiki=kikkikikkik ikikkiki*kkikk=ikkikikkiki ikikkiki*ikikik=ikkikikkik ikikkiki*ikikki=ikkik ikikkiki*ikkiki=ik ikikkiki*ikkikk=ikik ikikkiki*kikikk=ikikik ikikkiki*kikkik=kkikikki ikikkiki*kkikik=kkikikkiki ikikkiki*ikikikk=kikikkiki ikikkiki*ikikkik=ikkikk ikikkiki*ikkikik=ikk ikikkiki*kikikki=kkikk ikikkiki*kikkiki=kkikikk ikikkiki*kikkikk=kkikikkik ikikkiki*kkikikk=kikkikikki ikikkiki*ikikikki=kikikkik ikikkiki*ikikkiki=kikik ikikkiki*ikikkikk=ikki ikikkiki*ikkikikk=i ikikkiki*kikikkik=kki ikikkiki*kikkikik=kkiki ikikkiki*kkikikki=kikkikikk ikikkiki*ikikikkik=kikikkikk ikikkiki*ikikkikik=kikikk ikikkiki*ikkikikki=1 ikikkiki*kikikkiki=kk ikikkiki*kikikkikk=kkik ikikkiki*kikkikikk=kkikik ikikkiki*kkikikkik=kikkiki ikikkiki*ikikikkiki=kikkikik ikikkiki*ikikikkikk=kikikki ikikkiki*ikikkikikk=kiki ikikkiki*ikkikikkik=k ikikkiki*kikkikikki=kikkikk ikikkiki*kkikikkiki=kikkik ikikkiki*ikikkikikki=kik ikikkiki*ikkikikkiki=ki ikikkiki*kikkikikkik=kikki ikikkiki*ikikkikikkik=kikk ikikkikk*1=ikikkikk ikikkikk*i=ikkikik ikikkikk*k=ikikki ikikkikk*ik=ikkikikk ikikkikk*ki=ikikk ikikkikk*kk=ikikkik ikikkikk*iki=ikkikikki ikikkikk*ikk=ikkiki ikikkikk*kik=iki ikikkikk*kki=ikikkiki ikikkikk*ikik=ikkikikkik ikikkikk*ikki=ikkik ikikkikk*kiki=ik ikikkikk*kikk=ikik ikikkikk*kkik=ikikkikik ikikkikk*ikiki=ikkikikkiki ikikkikk*ikikk=kikikkiki ikikkikk*ikkik=ikkikk ikikkikk*kikik=ikk ikikkikk*kikki=ikiki ikikkikk*kkiki=ikikikkikk ikikkikk*kkikk=ikikkikikk ikikkikk*ikikik=ikikkikikki ikikkikk*ikikki=kikikkik ikikkikk*ikkiki=kikik ikikkikk*ikkikk=ikki ikikkikk*kikikk=i ikikkikk*kikkik=ikikik ikikkikk*kkikik=ikikikki ikikkikk*ikikikk=ikikkikikkik ikikkikk*ikikkik=kikikkikk ikikkikk*ikkikik=kikikk ikikkikk*kikikki=1 ikikkikk*kikkiki=kkikk ikikkikk*kikkikk=ikikikk ikikkikk*kkikikk=ikikikkik ikikkikk*ikikikki=kikkikikkik ikikkikk*ikikkiki=kikkikik ikikkikk*ikikkikk=kikikki ikikkikk*ikkikikk=kiki ikikkikk*kikikkik=k ikikkikk*kikkikik=kki ikikkikk*kkikikki=ikikikkiki ikikkikk*ikikikkik=kkikikkiki ikikkikk*ikikkikik=kikkikikk ikikkikk*ikkikikki=kik ikikkikk*kikikkiki=ki ikikkikk*kikikkikk=kk ikikkikk*kikkikikk=kkik ikikkikk*kkikikkik=kkikikki ikikkikk*ikikikkiki=kkikikkik ikikkikk*ikikikkikk=kikkikikki ikikkikk*ikikkikikk=kikkiki ikikkikk*ikkikikkik=kikk ikikkikk*kikkikikki=kkiki ikikkikk*kkikikkiki=kkikikk ikikkikk*ikikkikikki=kikkik ikikkikk*ikkikikkiki=kikki ikikkikk*kikkikikkik=kkikik ikikkikk*ikikkikikkik=kikkikk ikkikikk*1=ikkikikk ikkikikk*i=ikkikikki ikkikikk*k=ikkiki ikkikikk*ik=ikkikikkik ikkikikk*ki=ikkik ikkikikk*kk=ikkikik ikkikikk*iki=ikkikikkiki ikkikikk*ikk=kikikkiki ikkikikk*kik=ikkikk ikkikikk*kki=ikikkikk ikkikikk*ikik=ikikkikikki ikkikikk*ikki=kikikkik ikkikikk*kiki=kikik ikkikikk*kikk=ikki ikkikikk*kkik=ikikki ikkikikk*ikiki=ikikkikikk ikkikikk*ikikk=ikikkikikkik ikkikikk*ikkik=kikikkikk ikkikikk*kikik=kikikk ikkikikk*kikki=ikk ikkikikk*kkiki=ikikk ikkikikk*kkikk=ikikkik ikkikikk*ikikik=ikikkiki ikkikikk*ikikki=kikkikikkik ikkikikk*ikkiki=kikkikik ikkikikk*ikkikk=kikikki ikkikikk*kikikk=kiki ikkikikk*kikkik=i ikkikikk*kkikik=iki ikkikikk*ikikikk=ikikkikik ikkikikk*ikikkik=kkikikkiki ikkikikk*ikkikik=kikkikikk ikkikikk*kikikki=kik ikkikikk*kikkiki=1 ikkikikk*kikkikk=ik ikkikikk*kkikikk=ikik ikkikikk*ikikikki=ikikikkikk ikkikikk*ikikkiki=kkikikkik ikkikikk*ikikkikk=kikkikikki ikkikikk*ikkikikk=kikkiki ikkikikk*kikikkik=kikk ikkikikk*kikkikik=k ikkikikk*kkikikki=ikiki ikkikikk*ikikikkik=ikikikki ikkikikk*ikikkikik=ikikikkiki ikkikikk*ikkikikki=kikkik ikkikikk*kikikkiki=kikki ikkikikk*kikikkikk=ki ikkikikk*kikkikikk=kk ikkikikk*kkikikkik=ikikik ikkikikk*ikikikkiki=ikikikk ikkikikk*ikikikkikk=ikikikkik ikkikikk*ikikkikikk=kkikikki ikkikikk*ikkikikkik=kikkikk ikkikikk*kikkikikki=kki ikkikikk*kkikikkiki=kkikk ikkikikk*ikikkikikki=kkikikk ikkikikk*ikkikikkiki=kkikik ikkikikk*kikkikikkik=kkik ikkikikk*ikikkikikkik=kkiki kikikkik*1=kikikkik kikikkik*i=kikikkiki kikikkik*k=kikikkikk kikikkik*ik=ikkikikki kikikkik*ki=kikkikik kikikkik*kk=kikikki kikikkik*iki=ikkikikk kikikkik*ikk=ikkikikkik kikikkik*kik=kikkikikk kikikkik*kki=kikikk kikikkik*ikik=ikkiki kikikkik*ikki=ikkikikkiki kikikkik*kiki=kikkikikki kikikkik*kikk=kikkiki kikikkik*kkik=kiki kikikkik*ikiki=ikkik kikikkik*ikikk=ikkikik kikikkik*ikkik=ikikkikikki kikikkik*kikik=kikkikikkik kikikkik*kikki=kikkik kikikkik*kkiki=kik kikikkik*kkikk=kikik kikikkik*ikikik=ikkikk kikikkik*ikikki=ikikkikk kikikkik*ikkiki=ikikkikikk kikikkik*ikkikk=ikikkikikkik kikikkik*kikikk=kkikikkiki kikikkik*kikkik=kikkikk kikikkik*kkikik=kikk kikikkik*ikikikk=ikki kikikkik*ikikkik=ikikki kikikkik*ikkikik=ikikkiki kikikkik*kikikki=kkikikkik kikikkik*kikkiki=kkikik kikikkik*kikkikk=kikki kikikkik*kkikikk=ki kikikkik*ikikikki=ikk kikikkik*ikikkiki=ikikk kikikkik*ikikkikk=ikikkik kikikkik*ikkikikk=ikikkikik kikikkik*kikikkik=ikikikkiki kikikkik*kikkikik=kkikikk kikikkik*kkikikki=k kikikkik*ikikikkik=i kikikkik*ikikkikik=iki kikikkik*ikkikikki=ikikikkikk kikikkik*kikikkiki=ikikikkik kikikkik*kikikkikk=kkikikki kikikkik*kikkikikk=kkiki kikikkik*kkikikkik=kk kikikkik*ikikikkiki=1 kikikkik*ikikikkikk=ik kikikkik*ikikkikikk=ikik kikikkik*ikkikikkik=ikikikki kikikkik*kikkikikki=kkik kikikkik*kkikikkiki=kki kikikkik*ikikkikikki=ikiki kikikkik*ikkikikkiki=ikikikk kikikkik*kikkikikkik=kkikk kikikkik*ikikkikikkik=ikikik kikkikik*1=kikkikik kikkikik*i=kikikkikk kikkikik*k=kikkikikk kikkikik*ik=kikikki kikkikik*ki=kikkikikki kikkikik*kk=kikkiki kikkikik*iki=kikikk kikkikik*ikk=kikikkik kikkikik*kik=kikkikikkik kikkikik*kki=kikkik kikkikik*ikik=kiki kikkikik*ikki=kikikkiki kikkikik*kiki=ikikkikikkik kikkikik*kikk=kkikikkiki kikkikik*kkik=kikkikk kikkikik*ikiki=kik kikkikik*ikikk=kikik kikkikik*ikkik=ikkikikki kikkikik*kikik=ikkikikkiki kikkikik*kikki=kkikikkik kikkikik*kkiki=kkikik kikkikik*kkikk=kikki kikkikik*ikikik=kikk kikkikik*ikikki=ikkikk kikkikik*ikkiki=ikkikikk kikkikik*ikkikk=ikkikikkik kikkikik*kikikk=ikikkikikki kikkikik*kikkik=ikikikkiki kikkikik*kkikik=kkikikk kikkikik*ikikikk=ki kikkikik*ikikkik=ikki kikkikik*ikkikik=ikkiki kikkikik*kikikki=ikikkikikk kikkikik*kikkiki=ikikikkik kikkikik*kikkikk=kkikikki kikkikik*kkikikk=kkiki kikkikik*ikikikki=k kikkikik*ikikkiki=ikk kikkikik*ikikkikk=ikkik kikkikik*ikkikikk=ikkikik kikkikik*kikikkik=ikikkiki kikkikik*kikkikik=ikikikkikk kikkikik*kkikikki=kkik kikkikik*ikikikkik=kk kikkikik*ikikkikik=i kikkikik*ikkikikki=ikikkikk kikkikik*kikikkiki=ikikkik kikkikik*kikikkikk=ikikkikik kikkikik*kikkikikk=ikikikki kikkikik*kkikikkik=kkikk kikkikik*ikikikkiki=kki kikkikik*ikikikkikk=1 kikkikik*ikikkikikk=ik kikkikik*ikkikikkik=ikikki kikkikik*kikkikikki=ikikikk kikkikik*kkikikkiki=ikikik kikkikik*ikikkikikki=iki kikkikik*ikkikikkiki=ikikk kikkikik*kikkikikkik=ikiki kikkikik*ikikkikikkik=ikik kkikikki*1=kkikikki kkikikki*i=kkikikk kkikikki*k=kkikikkik kkikikki*ik=kkiki kkikikki*ki=kkikikkiki kkikikki*kk=ikikikkiki kkikikki*iki=kkik kkikikki*ikk=kkikik kkikikki*kik=kikkikikki kkikikki*kki=ikikikkik kkikikki*ikik=kkikk kkikikki*ikki=kikkikk kkikikki*kiki=kikkikikk kkikikki*kikk=kikkikikkik kkikikki*kkik=ikikikkikk kkikikki*ikiki=ikikik kkikikki*ikikk=kki kkikikki*ikkik=kikki kkikikki*kikik=kikkiki kkikikki*kikki=ikikkikikkik kkikikki*kkiki=ikikkikik kkikikki*kkikk=ikikikki kkikikki*ikikik=ikikikk kkikikki*ikikki=kk kkikikki*ikkiki=kikk kkikikki*ikkikk=kikkik kkikikki*kikikk=kikkikik kkikikki*kikkik=ikkikikkiki kkikikki*kkikik=ikikkikikk kkikikki*ikikikk=ikiki kkikikki*ikikkik=1 kkikikki*ikkikik=ki kkikikki*kikikki=kikikkikk kkikikki*kikkiki=ikkikikkik kkikikki*kikkikk=ikikkikikki kkikikki*kkikikk=ikikkiki kkikikki*ikikikki=ikik kkikikki*ikikkiki=i kkikikki*ikikkikk=k kkikikki*ikkikikk=kik kkikikki*kikikkik=kikikki kkikikki*kikkikik=kikikkiki kkikikki*kkikikki=ikikkik kkikikki*ikikikkik=ikikk kkikikki*ikikkikik=ik kkikikki*ikkikikki=kiki kkikikki*kikikkiki=kikikk kkikikki*kikikkikk=kikikkik kkikikki*kikkikikk=ikkikikki kkikikki*kkikikkik=ikikkikk kkikikki*ikikikkiki=ikikki kkikikki*ikikikkikk=iki kkikikki*ikikkikikk=ikk kkikikki*ikkikikkik=kikik kkikikki*kikkikikki=ikkikikk kkikikki*kkikikkiki=ikkikik kkikikki*ikikkikikki=ikki kkikikki*ikkikikkiki=ikkikk kkikikki*kikkikikkik=ikkiki kkikikki*ikikkikikkik=ikkik ikikikkik*1=ikikikkik ikikikkik*i=ikikikkiki ikikikkik*k=ikikikkikk ikikikkik*ik=kkikikki ikikikkik*ki=ikikkikik ikikikkik*kk=ikikikki ikikikkik*iki=kkikikk ikikikkik*ikk=kkikikkik ikikikkik*kik=ikikkikikk ikikikkik*kki=ikikikk ikikikkik*ikik=kkiki ikikikkik*ikki=kkikikkiki ikikikkik*kiki=ikikkikikki ikikikkik*kikk=ikikkiki ikikikkik*kkik=ikiki ikikikkik*ikiki=kkik ikikikkik*ikikk=kkikik ikikikkik*ikkik=kikkikikki ikikikkik*kikik=ikikkikikkik ikikikkik*kikki=ikikkik ikikikkik*kkiki=ikik ikikikkik*kkikk=ikikik ikikikkik*ikikik=kkikk ikikikkik*ikikki=kikkikk ikikikkik*ikkiki=kikkikikk ikikikkik*ikkikk=kikkikikkik ikikikkik*kikikk=ikkikikkiki ikikikkik*kikkik=ikikkikk ikikikkik*kkikik=ikikk ikikikkik*ikikikk=kki ikikikkik*ikikkik=kikki ikikikkik*ikkikik=kikkiki ikikikkik*kikikki=ikkikikkik ikikikkik*kikkiki=ikkikik ikikikkik*kikkikk=ikikki ikikikkik*kkikikk=iki ikikikkik*ikikikki=kk ikikikkik*ikikkiki=kikk ikikikkik*ikikkikk=kikkik ikikikkik*ikkikikk=kikkikik ikikikkik*kikikkik=kikikkiki ikikikkik*kikkikik=ikkikikk ikikikkik*kkikikki=ik ikikikkik*ikikikkik=1 ikikikkik*ikikkikik=ki ikikikkik*ikkikikki=kikikkikk ikikikkik*kikikkiki=kikikkik ikikikkik*kikikkikk=ikkikikki ikikikkik*kikkikikk=ikkiki ikikikkik*kkikikkik=ikk ikikikkik*ikikikkiki=i ikikikkik*ikikikkikk=k ikikikkik*ikikkikikk=kik ikikikkik*ikkikikkik=kikikki ikikikkik*kikkikikki=ikkik ikikikkik*kkikikkiki=ikki ikikikkik*ikikkikikki=kiki ikikikkik*ikkikikkiki=kikikk ikikikkik*kikkikikkik=ikkikk ikikikkik*ikikkikikkik=kikik ikikkikik*1=ikikkikik ikikkikik*i=ikikikkikk ikikkikik*k=ikikkikikk ikikkikik*ik=ikikikki ikikkikik*ki=ikikkikikki ikikkikik*kk=ikikkiki ikikkikik*iki=ikikikk ikikkikik*ikk=ikikikkik ikikkikik*kik=ikikkikikkik ikikkikik*kki=ikikkik ikikkikik*ikik=ikiki ikikkikik*ikki=ikikikkiki ikikkikik*kiki=kikkikikkik ikikkikik*kikk=ikkikikkiki ikikkikik*kkik=ikikkikk ikikkikik*ikiki=ikik ikikkikik*ikikk=ikikik ikikkikik*ikkik=kkikikki ikikkikik*kikik=kkikikkiki ikikkikik*kikki=ikkikikkik ikikkikik*kkiki=ikkikik ikikkikik*kkikk=ikikki ikikkikik*ikikik=ikikk ikikkikik*ikikki=kkikk ikikkikik*ikkiki=kkikikk ikikkikik*ikkikk=kkikikkik ikikkikik*kikikk=kikkikikki ikikkikik*kikkik=kikikkiki ikikkikik*kkikik=ikkikikk ikikkikik*ikikikk=iki ikikkikik*ikikkik=kki ikikkikik*ikkikik=kkiki ikikkikik*kikikki=kikkikikk ikikkikik*kikkiki=kikikkik ikikkikik*kikkikk=ikkikikki ikikkikik*kkikikk=ikkiki ikikkikik*ikikikki=ik ikikkikik*ikikkiki=kk ikikkikik*ikikkikk=kkik ikikkikik*ikkikikk=kkikik ikikkikik*kikikkik=kikkiki ikikkikik*kikkikik=kikikkikk ikikkikik*kkikikki=ikkik ikikkikik*ikikikkik=ikk ikikkikik*ikikkikik=1 ikikkikik*ikkikikki=kikkikk ikikkikik*kikikkiki=kikkik ikikkikik*kikikkikk=kikkikik ikikkikik*kikkikikk=kikikki ikikkikik*kkikikkik=ikkikk ikikkikik*ikikikkiki=ikki ikikkikik*ikikikkikk=i ikikkikik*ikikkikikk=k ikikkikik*ikkikikkik=kikki ikikkikik*kikkikikki=kikikk ikikkikik*kkikikkiki=kikik ikikkikik*ikikkikikki=ki ikikkikik*ikkikikkiki=kikk ikikkikik*kikkikikkik=kiki ikikkikik*ikikkikikkik=kik ikkikikki*1=ikkikikki ikkikikki*i=ikkikikk ikkikikki*k=ikkikikkik ikkikikki*ik=ikkiki ikkikikki*ki=ikkikikkiki ikkikikki*kk=kikikkiki ikkikikki*iki=ikkik ikkikikki*ikk=ikkikik ikkikikki*kik=ikikkikikki ikkikikki*kki=kikikkik ikkikikki*ikik=ikkikk ikkikikki*ikki=ikikkikk ikkikikki*kiki=ikikkikikk ikkikikki*kikk=ikikkikikkik ikkikikki*kkik=kikikkikk ikkikikki*ikiki=kikik ikkikikki*ikikk=ikki ikkikikki*ikkik=ikikki ikkikikki*kikik=ikikkiki ikkikikki*kikki=kikkikikkik ikkikikki*kkiki=kikkikik ikkikikki*kkikk=kikikki ikkikikki*ikikik=kikikk ikkikikki*ikikki=ikk ikkikikki*ikkiki=ikikk ikkikikki*ikkikk=ikikkik ikkikikki*kikikk=ikikkikik ikkikikki*kikkik=kkikikkiki ikkikikki*kkikik=kikkikikk ikkikikki*ikikikk=kiki ikkikikki*ikikkik=i ikkikikki*ikkikik=iki ikkikikki*kikikki=ikikikkikk ikkikikki*kikkiki=kkikikkik ikkikikki*kikkikk=kikkikikki ikkikikki*kkikikk=kikkiki ikkikikki*ikikikki=kik ikkikikki*ikikkiki=1 ikkikikki*ikikkikk=ik ikkikikki*ikkikikk=ikik ikkikikki*kikikkik=ikikikki ikkikikki*kikkikik=ikikikkiki ikkikikki*kkikikki=kikkik ikkikikki*ikikikkik=kikk ikkikikki*ikikkikik=k ikkikikki*ikkikikki=ikiki ikkikikki*kikikkiki=ikikikk ikkikikki*kikikkikk=ikikikkik ikkikikki*kikkikikk=kkikikki ikkikikki*kkikikkik=kikkikk ikkikikki*ikikikkiki=kikki ikkikikki*ikikikkikk=ki ikkikikki*ikikkikikk=kk ikkikikki*ikkikikkik=ikikik ikkikikki*kikkikikki=kkikikk ikkikikki*kkikikkiki=kkikik ikkikikki*ikikkikikki=kki ikkikikki*ikkikikkiki=kkikk ikkikikki*kikkikikkik=kkiki ikkikikki*ikikkikikkik=kkik kikikkiki*1=kikikkiki kikikkiki*i=kikikkik kikikkiki*k=ikkikikki kikikkiki*ik=kikikkikk kikikkiki*ki=ikkikikk kikikkiki*kk=ikkikikkik kikikkiki*iki=kikkikik kikikkiki*ikk=kikikki kikikkiki*kik=ikkiki kikikkiki*kki=ikkikikkiki kikikkiki*ikik=kikkikikk kikikkiki*ikki=kikikk kikikkiki*kiki=ikkik kikikkiki*kikk=ikkikik kikikkiki*kkik=ikikkikikki kikikkiki*ikiki=kikkikikki kikikkiki*ikikk=kikkiki kikikkiki*ikkik=kiki kikikkiki*kikik=ikkikk kikikkiki*kikki=ikikkikk kikikkiki*kkiki=ikikkikikk kikikkiki*kkikk=ikikkikikkik kikikkiki*ikikik=kikkikikkik kikikkiki*ikikki=kikkik kikikkiki*ikkiki=kik kikikkiki*ikkikk=kikik kikikkiki*kikikk=ikki kikikkiki*kikkik=ikikki kikikkiki*kkikik=ikikkiki kikikkiki*ikikikk=kkikikkiki kikikkiki*ikikkik=kikkikk kikikkiki*ikkikik=kikk kikikkiki*kikikki=ikk kikikkiki*kikkiki=ikikk kikikkiki*kikkikk=ikikkik kikikkiki*kkikikk=ikikkikik kikikkiki*ikikikki=kkikikkik kikikkiki*ikikkiki=kkikik kikikkiki*ikikkikk=kikki kikikkiki*ikkikikk=ki kikikkiki*kikikkik=i kikikkiki*kikkikik=iki kikikkiki*kkikikki=ikikikkikk kikikkiki*ikikikkik=ikikikkiki kikikkiki*ikikkikik=kkikikk kikikkiki*ikkikikki=k kikikkiki*kikikkiki=1 kikikkiki*kikikkikk=ik kikikkiki*kikkikikk=ikik kikikkiki*kkikikkik=ikikikki kikikkiki*ikikikkiki=ikikikkik kikikkiki*ikikikkikk=kkikikki kikikkiki*ikikkikikk=kkiki kikikkiki*ikkikikkik=kk kikikkiki*kikkikikki=ikiki kikikkiki*kkikikkiki=ikikikk kikikkiki*ikikkikikki=kkik kikikkiki*ikkikikkiki=kki kikikkiki*kikkikikkik=ikikik kikikkiki*ikikkikikkik=kkikk kikikkikk*1=kikikkikk kikikkikk*i=kikkikik kikikkikk*k=kikikki kikikkikk*ik=kikkikikk kikikkikk*ki=kikikk kikikkikk*kk=kikikkik kikikkikk*iki=kikkikikki kikikkikk*ikk=kikkiki kikikkikk*kik=kiki kikikkikk*kki=kikikkiki kikikkikk*ikik=kikkikikkik kikikkikk*ikki=kikkik kikikkikk*kiki=kik kikikkikk*kikk=kikik kikikkikk*kkik=ikkikikki kikikkikk*ikiki=ikikkikikkik kikikkikk*ikikk=kkikikkiki kikikkikk*ikkik=kikkikk kikikkikk*kikik=kikk kikikkikk*kikki=ikkikk kikikkikk*kkiki=ikkikikk kikikkikk*kkikk=ikkikikkik kikikkikk*ikikik=ikkikikkiki kikikkikk*ikikki=kkikikkik kikikkikk*ikkiki=kkikik kikikkikk*ikkikk=kikki kikikkikk*kikikk=ki kikikkikk*kikkik=ikki kikikkikk*kkikik=ikkiki kikikkikk*ikikikk=ikikkikikki kikikkikk*ikikkik=ikikikkiki kikikkikk*ikkikik=kkikikk kikikkikk*kikikki=k kikikkikk*kikkiki=ikk kikikkikk*kikkikk=ikkik kikikkikk*kkikikk=ikkikik kikikkikk*ikikikki=ikikkikikk kikikkikk*ikikkiki=ikikikkik kikikkikk*ikikkikk=kkikikki kikikkikk*ikkikikk=kkiki kikikkikk*kikikkik=kk kikikkikk*kikkikik=i kikikkikk*kkikikki=ikikkikk kikikkikk*ikikikkik=ikikkiki kikikkikk*ikikkikik=ikikikkikk kikikkikk*ikkikikki=kkik kikikkikk*kikikkiki=kki kikikkikk*kikikkikk=1 kikikkikk*kikkikikk=ik kikikkikk*kkikikkik=ikikki kikikkikk*ikikikkiki=ikikkik kikikkikk*ikikikkikk=ikikkikik kikikkikk*ikikkikikk=ikikikki kikikkikk*ikkikikkik=kkikk kikikkikk*kikkikikki=iki kikikkikk*kkikikkiki=ikikk kikikkikk*ikikkikikki=ikikikk kikikkikk*ikkikikkiki=ikikik kikikkikk*kikkikikkik=ikik kikikkikk*ikikkikikkik=ikiki kikkikikk*1=kikkikikk kikkikikk*i=kikkikikki kikkikikk*k=kikkiki kikkikikk*ik=kikkikikkik kikkikikk*ki=kikkik kikkikikk*kk=kikkikik kikkikikk*iki=ikikkikikkik kikkikikk*ikk=kkikikkiki kikkikikk*kik=kikkikk kikkikikk*kki=kikikkikk kikkikikk*ikik=ikkikikkiki kikkikikk*ikki=kkikikkik kikkikikk*kiki=kkikik kikkikikk*kikk=kikki kikkikikk*kkik=kikikki kikkikikk*ikiki=ikkikikkik kikkikikk*ikikk=ikikkikikki kikkikikk*ikkik=ikikikkiki kikkikikk*kikik=kkikikk kikkikikk*kikki=kikk kikkikikk*kkiki=kikikk kikkikikk*kkikk=kikikkik kikkikikk*ikikik=kikikkiki kikkikikk*ikikki=ikikkikikk kikkikikk*ikkiki=ikikikkik kikkikikk*ikkikk=kkikikki kikkikikk*kikikk=kkiki kikkikikk*kikkik=ki kikkikikk*kkikik=kiki kikkikikk*ikikikk=ikkikikki kikkikikk*ikikkik=ikikkiki kikkikikk*ikkikik=ikikikkikk kikkikikk*kikikki=kkik kikkikikk*kikkiki=k kikkikikk*kikkikk=kik kikkikikk*kkikikk=kikik kikkikikk*ikikikki=ikkikikk kikkikikk*ikikkiki=ikikkik kikkikikk*ikikkikk=ikikkikik kikkikikk*ikkikikk=ikikikki kikkikikk*kikikkik=kkikk kikkikikk*kikkikik=kk kikkikikk*kkikikki=ikkikk kikkikikk*ikikikkik=ikkiki kikkikikk*ikikkikik=ikikkikk kikkikikk*ikkikikki=ikikikk kikkikikk*kikikkiki=ikikik kikkikikk*kikikkikk=kki kikkikikk*kikkikikk=1 kikkikikk*kkikikkik=ikki kikkikikk*ikikikkiki=ikkik kikkikikk*ikikikkikk=ikkikik kikkikikk*ikikkikikk=ikikki kikkikikk*ikkikikkik=ikiki kikkikikk*kikkikikki=i kikkikikk*kkikikkiki=ikk kikkikikk*ikikkikikki=ikikk kikkikikk*ikkikikkiki=ikik kikkikikk*kikkikikkik=ik kikkikikk*ikikkikikkik=iki kkikikkik*1=kkikikkik kkikikkik*i=kkikikkiki kkikikkik*k=ikikikkiki kkikikkik*ik=kikkikikki kkikikkik*ki=ikikikkik kkikikkik*kk=kkikikki kkikikkik*iki=kikkikikk kkikikkik*ikk=kikkikikkik kkikikkik*kik=ikikikkikk kkikikkik*kki=kkikikk kkikikkik*ikik=kikkiki kkikikkik*ikki=ikikkikikkik kkikikkik*kiki=ikikkikik kkikikkik*kikk=ikikikki kkikikkik*kkik=kkiki kkikikkik*ikiki=kikkik kkikikkik*ikikk=kikkikik kkikikkik*ikkik=ikkikikkiki kkikikkik*kikik=ikikkikikk kkikikkik*kikki=ikikikk kkikikkik*kkiki=kkik kkikikkik*kkikk=kkikik kkikikkik*ikikik=kikkikk kkikikkik*ikikki=kikikkikk kkikikkik*ikkiki=ikkikikkik kkikikkik*ikkikk=ikikkikikki kkikikkik*kikikk=ikikkiki kkikikkik*kikkik=ikiki kkikikkik*kkikik=kkikk kkikikkik*ikikikk=kikki kkikikkik*ikikkik=kikikki kkikikkik*ikkikik=kikikkiki kkikikkik*kikikki=ikikkik kkikikkik*kikkiki=ikik kkikikkik*kikkikk=ikikik kkikikkik*kkikikk=kki kkikikkik*ikikikki=kikk kkikikkik*ikikkiki=kikikk kkikikkik*ikikkikk=kikikkik kkikikkik*ikkikikk=ikkikikki kkikikkik*kikikkik=ikikkikk kkikikkik*kikkikik=ikikk kkikikkik*kkikikki=kk kkikikkik*ikikikkik=ki kkikikkik*ikikkikik=kiki kkikikkik*ikkikikki=ikkikikk kkikikkik*kikikkiki=ikkikik kkikikkik*kikikkikk=ikikki kkikikkik*kikkikikk=iki kkikikkik*kkikikkik=1 kkikikkik*ikikikkiki=k kkikikkik*ikikikkikk=kik kkikikkik*ikikkikikk=kikik kkikikkik*ikkikikkik=ikkiki kkikikkik*kikkikikki=ik kkikikkik*kkikikkiki=i kkikikkik*ikikkikikki=ikkikk kkikikkik*ikkikikkiki=ikkik kkikikkik*kikkikikkik=ikk kkikikkik*ikikkikikkik=ikki ikikikkiki*1=ikikikkiki ikikikkiki*i=ikikikkik ikikikkiki*k=kkikikki ikikikkiki*ik=ikikikkikk ikikikkiki*ki=kkikikk ikikikkiki*kk=kkikikkik ikikikkiki*iki=ikikkikik ikikikkiki*ikk=ikikikki ikikikkiki*kik=kkiki ikikikkiki*kki=kkikikkiki ikikikkiki*ikik=ikikkikikk ikikikkiki*ikki=ikikikk ikikikkiki*kiki=kkik ikikikkiki*kikk=kkikik ikikikkiki*kkik=kikkikikki ikikikkiki*ikiki=ikikkikikki ikikikkiki*ikikk=ikikkiki ikikikkiki*ikkik=ikiki ikikikkiki*kikik=kkikk ikikikkiki*kikki=kikkikk ikikikkiki*kkiki=kikkikikk ikikikkiki*kkikk=kikkikikkik ikikikkiki*ikikik=ikikkikikkik ikikikkiki*ikikki=ikikkik ikikikkiki*ikkiki=ikik ikikikkiki*ikkikk=ikikik ikikikkiki*kikikk=kki ikikikkiki*kikkik=kikki ikikikkiki*kkikik=kikkiki ikikikkiki*ikikikk=ikkikikkiki ikikikkiki*ikikkik=ikikkikk ikikikkiki*ikkikik=ikikk ikikikkiki*kikikki=kk ikikikkiki*kikkiki=kikk ikikikkiki*kikkikk=kikkik ikikikkiki*kkikikk=kikkikik ikikikkiki*ikikikki=ikkikikkik ikikikkiki*ikikkiki=ikkikik ikikikkiki*ikikkikk=ikikki ikikikkiki*ikkikikk=iki ikikikkiki*kikikkik=1 ikikikkiki*kikkikik=ki ikikikkiki*kkikikki=kikikkikk ikikikkiki*ikikikkik=kikikkiki ikikikkiki*ikikkikik=ikkikikk ikikikkiki*ikkikikki=ik ikikikkiki*kikikkiki=i ikikikkiki*kikikkikk=k ikikikkiki*kikkikikk=kik ikikikkiki*kkikikkik=kikikki ikikikkiki*ikikikkiki=kikikkik ikikikkiki*ikikikkikk=ikkikikki ikikikkiki*ikikkikikk=ikkiki ikikikkiki*ikkikikkik=ikk ikikikkiki*kikkikikki=kiki ikikikkiki*kkikikkiki=kikikk ikikikkiki*ikikkikikki=ikkik ikikikkiki*ikkikikkiki=ikki ikikikkiki*kikkikikkik=kikik ikikikkiki*ikikkikikkik=ikkikk ikikikkikk*1=ikikikkikk ikikikkikk*i=ikikkikik ikikikkikk*k=ikikikki ikikikkikk*ik=ikikkikikk ikikikkikk*ki=ikikikk ikikikkikk*kk=ikikikkik ikikikkikk*iki=ikikkikikki ikikikkikk*ikk=ikikkiki ikikikkikk*kik=ikiki ikikikkikk*kki=ikikikkiki ikikikkikk*ikik=ikikkikikkik ikikikkikk*ikki=ikikkik ikikikkikk*kiki=ikik ikikikkikk*kikk=ikikik ikikikkikk*kkik=kkikikki ikikikkikk*ikiki=kikkikikkik ikikikkikk*ikikk=ikkikikkiki ikikikkikk*ikkik=ikikkikk ikikikkikk*kikik=ikikk ikikikkikk*kikki=kkikk ikikikkikk*kkiki=kkikikk ikikikkikk*kkikk=kkikikkik ikikikkikk*ikikik=kkikikkiki ikikikkikk*ikikki=ikkikikkik ikikikkikk*ikkiki=ikkikik ikikikkikk*ikkikk=ikikki ikikikkikk*kikikk=iki ikikikkikk*kikkik=kki ikikikkikk*kkikik=kkiki ikikikkikk*ikikikk=kikkikikki ikikikkikk*ikikkik=kikikkiki ikikikkikk*ikkikik=ikkikikk ikikikkikk*kikikki=ik ikikikkikk*kikkiki=kk ikikikkikk*kikkikk=kkik ikikikkikk*kkikikk=kkikik ikikikkikk*ikikikki=kikkikikk ikikikkikk*ikikkiki=kikikkik ikikikkikk*ikikkikk=ikkikikki ikikikkikk*ikkikikk=ikkiki ikikikkikk*kikikkik=ikk ikikikkikk*kikkikik=1 ikikikkikk*kkikikki=kikkikk ikikikkikk*ikikikkik=kikkiki ikikikkikk*ikikkikik=kikikkikk ikikikkikk*ikkikikki=ikkik ikikikkikk*kikikkiki=ikki ikikikkikk*kikikkikk=i ikikikkikk*kikkikikk=k ikikikkikk*kkikikkik=kikki ikikikkikk*ikikikkiki=kikkik ikikikkikk*ikikikkikk=kikkikik ikikikkikk*ikikkikikk=kikikki ikikikkikk*ikkikikkik=ikkikk ikikikkikk*kikkikikki=ki ikikikkikk*kkikikkiki=kikk ikikikkikk*ikikkikikki=kikikk ikikikkikk*ikkikikkiki=kikik ikikikkikk*kikkikikkik=kik ikikikkikk*ikikkikikkik=kiki ikikkikikk*1=ikikkikikk ikikkikikk*i=ikikkikikki ikikkikikk*k=ikikkiki ikikkikikk*ik=ikikkikikkik ikikkikikk*ki=ikikkik ikikkikikk*kk=ikikkikik ikikkikikk*iki=kikkikikkik ikikkikikk*ikk=ikkikikkiki ikikkikikk*kik=ikikkikk ikikkikikk*kki=ikikikkikk ikikkikikk*ikik=kkikikkiki ikikkikikk*ikki=ikkikikkik ikikkikikk*kiki=ikkikik ikikkikikk*kikk=ikikki ikikkikikk*kkik=ikikikki ikikkikikk*ikiki=kkikikkik ikikkikikk*ikikk=kikkikikki ikikkikikk*ikkik=kikikkiki ikikkikikk*kikik=ikkikikk ikikkikikk*kikki=ikikk ikikkikikk*kkiki=ikikikk ikikkikikk*kkikk=ikikikkik ikikkikikk*ikikik=ikikikkiki ikikkikikk*ikikki=kikkikikk ikikkikikk*ikkiki=kikikkik ikikkikikk*ikkikk=ikkikikki ikikkikikk*kikikk=ikkiki ikikkikikk*kikkik=iki ikikkikikk*kkikik=ikiki ikikkikikk*ikikikk=kkikikki ikikkikikk*ikikkik=kikkiki ikikkikikk*ikkikik=kikikkikk ikikkikikk*kikikki=ikkik ikikkikikk*kikkiki=ik ikikkikikk*kikkikk=ikik ikikkikikk*kkikikk=ikikik ikikkikikk*ikikikki=kkikikk ikikkikikk*ikikkiki=kikkik ikikkikikk*ikikkikk=kikkikik ikikkikikk*ikkikikk=kikikki ikikkikikk*kikikkik=ikkikk ikikkikikk*kikkikik=ikk ikikkikikk*kkikikki=kkikk ikikkikikk*ikikikkik=kkiki ikikkikikk*ikikkikik=kikkikk ikikkikikk*ikkikikki=kikikk ikikkikikk*kikikkiki=kikik ikikkikikk*kikikkikk=ikki ikikkikikk*kikkikikk=i ikikkikikk*kkikikkik=kki ikikkikikk*ikikikkiki=kkik ikikkikikk*ikikikkikk=kkikik ikikkikikk*ikikkikikk=kikki ikikkikikk*ikkikikkik=kiki ikikkikikk*kikkikikki=1 ikikkikikk*kkikikkiki=kk ikikkikikk*ikikkikikki=kikk ikikkikikk*ikkikikkiki=kik ikikkikikk*kikkikikkik=k ikikkikikk*ikikkikikkik=ki ikkikikkik*1=ikkikikkik ikkikikkik*i=ikkikikkiki ikkikikkik*k=kikikkiki ikkikikkik*ik=ikikkikikki ikkikikkik*ki=kikikkik ikkikikkik*kk=ikkikikki ikkikikkik*iki=ikikkikikk ikkikikkik*ikk=ikikkikikkik ikkikikkik*kik=kikikkikk ikkikikkik*kki=ikkikikk ikkikikkik*ikik=ikikkiki ikkikikkik*ikki=kikkikikkik ikkikikkik*kiki=kikkikik ikkikikkik*kikk=kikikki ikkikikkik*kkik=ikkiki ikkikikkik*ikiki=ikikkik ikkikikkik*ikikk=ikikkikik ikkikikkik*ikkik=kkikikkiki ikkikikkik*kikik=kikkikikk ikkikikkik*kikki=kikikk ikkikikkik*kkiki=ikkik ikkikikkik*kkikk=ikkikik ikkikikkik*ikikik=ikikkikk ikkikikkik*ikikki=ikikikkikk ikkikikkik*ikkiki=kkikikkik ikkikikkik*ikkikk=kikkikikki ikkikikkik*kikikk=kikkiki ikkikikkik*kikkik=kiki ikkikikkik*kkikik=ikkikk ikkikikkik*ikikikk=ikikki ikkikikkik*ikikkik=ikikikki ikkikikkik*ikkikik=ikikikkiki ikkikikkik*kikikki=kikkik ikkikikkik*kikkiki=kik ikkikikkik*kikkikk=kikik ikkikikkik*kkikikk=ikki ikkikikkik*ikikikki=ikikk ikkikikkik*ikikkiki=ikikikk ikkikikkik*ikikkikk=ikikikkik ikkikikkik*ikkikikk=kkikikki ikkikikkik*kikikkik=kikkikk ikkikikkik*kikkikik=kikk ikkikikkik*kkikikki=ikk ikkikikkik*ikikikkik=iki ikkikikkik*ikikkikik=ikiki ikkikikkik*ikkikikki=kkikikk ikkikikkik*kikikkiki=kkikik ikkikikkik*kikikkikk=kikki ikkikikkik*kikkikikk=ki ikkikikkik*kkikikkik=i ikkikikkik*ikikikkiki=ik ikkikikkik*ikikikkikk=ikik ikkikikkik*ikikkikikk=ikikik ikkikikkik*ikkikikkik=kkiki ikkikikkik*kikkikikki=k ikkikikkik*kkikikkiki=1 ikkikikkik*ikikkikikki=kkikk ikkikikkik*ikkikikkiki=kkik ikkikikkik*kikkikikkik=kk ikkikikkik*ikikkikikkik=kki kikkikikki*1=kikkikikki kikkikikki*i=kikkikikk kikkikikki*k=kikkikikkik kikkikikki*ik=kikkiki kikkikikki*ki=ikikkikikkik kikkikikki*kk=kkikikkiki kikkikikki*iki=kikkik kikkikikki*ikk=kikkikik kikkikikki*kik=ikkikikkiki kikkikikki*kki=kkikikkik kikkikikki*ikik=kikkikk kikkikikki*ikki=kikikkikk kikkikikki*kiki=ikkikikkik kikkikikki*kikk=ikikkikikki kikkikikki*kkik=ikikikkiki kikkikikki*ikiki=kkikik kikkikikki*ikikk=kikki kikkikikki*ikkik=kikikki kikkikikki*kikik=kikikkiki kikkikikki*kikki=ikikkikikk kikkikikki*kkiki=ikikikkik kikkikikki*kkikk=kkikikki kikkikikki*ikikik=kkikikk kikkikikki*ikikki=kikk kikkikikki*ikkiki=kikikk kikkikikki*ikkikk=kikikkik kikkikikki*kikikk=ikkikikki kikkikikki*kikkik=ikikkiki kikkikikki*kkikik=ikikikkikk kikkikikki*ikikikk=kkiki kikkikikki*ikikkik=ki kikkikikki*ikkikik=kiki kikkikikki*kikikki=ikkikikk kikkikikki*kikkiki=ikikkik kikkikikki*kikkikk=ikikkikik kikkikikki*kkikikk=ikikikki kikkikikki*ikikikki=kkik kikkikikki*ikikkiki=k kikkikikki*ikikkikk=kik kikkikikki*ikkikikk=kikik kikkikikki*kikikkik=ikkiki kikkikikki*kikkikik=ikikkikk kikkikikki*kkikikki=ikikikk kikkikikki*ikikikkik=kkikk kikkikikki*ikikkikik=kk kikkikikki*ikkikikki=ikkikk kikkikikki*kikikkiki=ikkik kikkikikki*kikikkikk=ikkikik kikkikikki*kikkikikk=ikikki kikkikikki*kkikikkik=ikiki kikkikikki*ikikikkiki=ikikik kikkikikki*ikikikkikk=kki kikkikikki*ikikkikikk=1 kikkikikki*ikkikikkik=ikki kikkikikki*kikkikikki=ikikk kikkikikki*kkikikkiki=ikik kikkikikki*ikikkikikki=i kikkikikki*ikkikikkiki=ikk kikkikikki*kikkikikkik=iki kikkikikki*ikikkikikkik=ik kkikikkiki*1=kkikikkiki kkikikkiki*i=kkikikkik kkikikkiki*k=kikkikikki kkikikkiki*ik=ikikikkiki kkikikkiki*ki=kikkikikk kkikikkiki*kk=kikkikikkik kkikikkiki*iki=ikikikkik kkikikkiki*ikk=kkikikki kkikikkiki*kik=kikkiki kkikikkiki*kki=ikikkikikkik kkikikkiki*ikik=ikikikkikk kkikikkiki*ikki=kkikikk kkikikkiki*kiki=kikkik kkikikkiki*kikk=kikkikik kkikikkiki*kkik=ikkikikkiki kkikikkiki*ikiki=ikikkikik kkikikkiki*ikikk=ikikikki kkikikkiki*ikkik=kkiki kkikikkiki*kikik=kikkikk kkikikkiki*kikki=kikikkikk kkikikkiki*kkiki=ikkikikkik kkikikkiki*kkikk=ikikkikikki kkikikkiki*ikikik=ikikkikikk kkikikkiki*ikikki=ikikikk kkikikkiki*ikkiki=kkik kkikikkiki*ikkikk=kkikik kkikikkiki*kikikk=kikki kkikikkiki*kikkik=kikikki kkikikkiki*kkikik=kikikkiki kkikikkiki*ikikikk=ikikkiki kkikikkiki*ikikkik=ikiki kkikikkiki*ikkikik=kkikk kkikikkiki*kikikki=kikk kkikikkiki*kikkiki=kikikk kkikikkiki*kikkikk=kikikkik kkikikkiki*kkikikk=ikkikikki kkikikkiki*ikikikki=ikikkik kkikikkiki*ikikkiki=ikik kkikikkiki*ikikkikk=ikikik kkikikkiki*ikkikikk=kki kkikikkiki*kikikkik=ki kkikikkiki*kikkikik=kiki kkikikkiki*kkikikki=ikkikikk kkikikkiki*ikikikkik=ikikkikk kkikikkiki*ikikkikik=ikikk kkikikkiki*ikkikikki=kk kkikikkiki*kikikkiki=k kkikikkiki*kikikkikk=kik kkikikkiki*kikkikikk=kikik kkikikkiki*kkikikkik=ikkiki kkikikkiki*ikikikkiki=ikkikik kkikikkiki*ikikikkikk=ikikki kkikikkiki*ikikkikikk=iki kkikikkiki*ikkikikkik=1 kkikikkiki*kikkikikki=ikkikk kkikikkiki*kkikikkiki=ikkik kkikikkiki*ikikkikikki=ik kkikikkiki*ikkikikkiki=i kkikikkiki*kikkikikkik=ikki kkikikkiki*ikikkikikkik=ikk ikikkikikki*1=ikikkikikki ikikkikikki*i=ikikkikikk ikikkikikki*k=ikikkikikkik ikikkikikki*ik=ikikkiki ikikkikikki*ki=kikkikikkik ikikkikikki*kk=ikkikikkiki ikikkikikki*iki=ikikkik ikikkikikki*ikk=ikikkikik ikikkikikki*kik=kkikikkiki ikikkikikki*kki=ikkikikkik ikikkikikki*ikik=ikikkikk ikikkikikki*ikki=ikikikkikk ikikkikikki*kiki=kkikikkik ikikkikikki*kikk=kikkikikki ikikkikikki*kkik=kikikkiki ikikkikikki*ikiki=ikkikik ikikkikikki*ikikk=ikikki ikikkikikki*ikkik=ikikikki ikikkikikki*kikik=ikikikkiki ikikkikikki*kikki=kikkikikk ikikkikikki*kkiki=kikikkik ikikkikikki*kkikk=ikkikikki ikikkikikki*ikikik=ikkikikk ikikkikikki*ikikki=ikikk ikikkikikki*ikkiki=ikikikk ikikkikikki*ikkikk=ikikikkik ikikkikikki*kikikk=kkikikki ikikkikikki*kikkik=kikkiki ikikkikikki*kkikik=kikikkikk ikikkikikki*ikikikk=ikkiki ikikkikikki*ikikkik=iki ikikkikikki*ikkikik=ikiki ikikkikikki*kikikki=kkikikk ikikkikikki*kikkiki=kikkik ikikkikikki*kikkikk=kikkikik ikikkikikki*kkikikk=kikikki ikikkikikki*ikikikki=ikkik ikikkikikki*ikikkiki=ik ikikkikikki*ikikkikk=ikik ikikkikikki*ikkikikk=ikikik ikikkikikki*kikikkik=kkiki ikikkikikki*kikkikik=kikkikk ikikkikikki*kkikikki=kikikk ikikkikikki*ikikikkik=ikkikk ikikkikikki*ikikkikik=ikk ikikkikikki*ikkikikki=kkikk ikikkikikki*kikikkiki=kkik ikikkikikki*kikikkikk=kkikik ikikkikikki*kikkikikk=kikki ikikkikikki*kkikikkik=kiki ikikkikikki*ikikikkiki=kikik ikikkikikki*ikikikkikk=ikki ikikkikikki*ikikkikikk=i ikikkikikki*ikkikikkik=kki ikikkikikki*kikkikikki=kikk ikikkikikki*kkikikkiki=kik ikikkikikki*ikikkikikki=1 ikikkikikki*ikkikikkiki=kk ikikkikikki*kikkikikkik=ki ikikkikikki*ikikkikikkik=k ikkikikkiki*1=ikkikikkiki ikkikikkiki*i=ikkikikkik ikkikikkiki*k=ikikkikikki ikkikikkiki*ik=kikikkiki ikkikikkiki*ki=ikikkikikk ikkikikkiki*kk=ikikkikikkik ikkikikkiki*iki=kikikkik ikkikikkiki*ikk=ikkikikki ikkikikkiki*kik=ikikkiki ikkikikkiki*kki=kikkikikkik ikkikikkiki*ikik=kikikkikk ikkikikkiki*ikki=ikkikikk ikkikikkiki*kiki=ikikkik ikkikikkiki*kikk=ikikkikik ikkikikkiki*kkik=kkikikkiki ikkikikkiki*ikiki=kikkikik ikkikikkiki*ikikk=kikikki ikkikikkiki*ikkik=ikkiki ikkikikkiki*kikik=ikikkikk ikkikikkiki*kikki=ikikikkikk ikkikikkiki*kkiki=kkikikkik ikkikikkiki*kkikk=kikkikikki ikkikikkiki*ikikik=kikkikikk ikkikikkiki*ikikki=kikikk ikkikikkiki*ikkiki=ikkik ikkikikkiki*ikkikk=ikkikik ikkikikkiki*kikikk=ikikki ikkikikkiki*kikkik=ikikikki ikkikikkiki*kkikik=ikikikkiki ikkikikkiki*ikikikk=kikkiki ikkikikkiki*ikikkik=kiki ikkikikkiki*ikkikik=ikkikk ikkikikkiki*kikikki=ikikk ikkikikkiki*kikkiki=ikikikk ikkikikkiki*kikkikk=ikikikkik ikkikikkiki*kkikikk=kkikikki ikkikikkiki*ikikikki=kikkik ikkikikkiki*ikikkiki=kik ikkikikkiki*ikikkikk=kikik ikkikikkiki*ikkikikk=ikki ikkikikkiki*kikikkik=iki ikkikikkiki*kikkikik=ikiki ikkikikkiki*kkikikki=kkikikk ikkikikkiki*ikikikkik=kikkikk ikkikikkiki*ikikkikik=kikk ikkikikkiki*ikkikikki=ikk ikkikikkiki*kikikkiki=ik ikkikikkiki*kikikkikk=ikik ikkikikkiki*kikkikikk=ikikik ikkikikkiki*kkikikkik=kkiki ikkikikkiki*ikikikkiki=kkikik ikkikikkiki*ikikikkikk=kikki ikkikikkiki*ikikkikikk=ki ikkikikkiki*ikkikikkik=i ikkikikkiki*kikkikikki=kkikk ikkikikkiki*kkikikkiki=kkik ikkikikkiki*ikikkikikki=k ikkikikkiki*ikkikikkiki=1 ikkikikkiki*kikkikikkik=kki ikkikikkiki*ikikkikikkik=kk kikkikikkik*1=kikkikikkik kikkikikkik*i=ikikkikikkik kikkikikkik*k=kkikikkiki kikkikikkik*ik=ikkikikkiki kikkikikkik*ki=kkikikkik kikkikikkik*kk=kikkikikki kikkikikkik*iki=ikkikikkik kikkikikkik*ikk=ikikkikikki kikkikikkik*kik=ikikikkiki kikkikikkik*kki=kikkikikk kikkikikkik*ikik=kikikkiki kikkikikkik*ikki=ikikkikikk kikkikikkik*kiki=ikikikkik kikkikikkik*kikk=kkikikki kikkikikkik*kkik=kikkiki kikkikikkik*ikiki=kikikkik kikkikikkik*ikikk=ikkikikki kikkikikkik*ikkik=ikikkiki kikkikikkik*kikik=ikikikkikk kikkikikkik*kikki=kkikikk kikkikikkik*kkiki=kikkik kikkikikkik*kkikk=kikkikik kikkikikkik*ikikik=kikikkikk kikkikikkik*ikikki=ikkikikk kikkikikkik*ikkiki=ikikkik kikkikikkik*ikkikk=ikikkikik kikkikikkik*kikikk=ikikikki kikkikikkik*kikkik=kkiki kikkikikkik*kkikik=kikkikk kikkikikkik*ikikikk=kikikki kikkikikkik*ikikkik=ikkiki kikkikikkik*ikkikik=ikikkikk kikkikikkik*kikikki=ikikikk kikkikikkik*kikkiki=kkik kikkikikkik*kikkikk=kkikik kikkikikkik*kkikikk=kikki kikkikikkik*ikikikki=kikikk kikkikikkik*ikikkiki=ikkik kikkikikkik*ikikkikk=ikkikik kikkikikkik*ikkikikk=ikikki kikkikikkik*kikikkik=ikiki kikkikikkik*kikkikik=kkikk kikkikikkik*kkikikki=kikk kikkikikkik*ikikikkik=kiki kikkikikkik*ikikkikik=ikkikk kikkikikkik*ikkikikki=ikikk kikkikikkik*kikikkiki=ikik kikkikikkik*kikikkikk=ikikik kikkikikkik*kikkikikk=kki kikkikikkik*kkikikkik=ki kikkikikkik*ikikikkiki=kik kikkikikkik*ikikikkikk=kikik kikkikikkik*ikikkikikk=ikki kikkikikkik*ikkikikkik=iki kikkikikkik*kikkikikki=kk kikkikikkik*kkikikkiki=k kikkikikkik*ikikkikikki=ikk kikkikikkik*ikkikikkiki=ik kikkikikkik*kikkikikkik=1 kikkikikkik*ikikkikikkik=i ikikkikikkik*1=ikikkikikkik ikikkikikkik*i=kikkikikkik ikikkikikkik*k=ikkikikkiki ikikkikikkik*ik=kkikikkiki ikikkikikkik*ki=ikkikikkik ikikkikikkik*kk=ikikkikikki ikikkikikkik*iki=kkikikkik ikikkikikkik*ikk=kikkikikki ikikkikikkik*kik=kikikkiki ikikkikikkik*kki=ikikkikikk ikikkikikkik*ikik=ikikikkiki ikikkikikkik*ikki=kikkikikk ikikkikikkik*kiki=kikikkik ikikkikikkik*kikk=ikkikikki ikikkikikkik*kkik=ikikkiki ikikkikikkik*ikiki=ikikikkik ikikkikikkik*ikikk=kkikikki ikikkikikkik*ikkik=kikkiki ikikkikikkik*kikik=kikikkikk ikikkikikkik*kikki=ikkikikk ikikkikikkik*kkiki=ikikkik ikikkikikkik*kkikk=ikikkikik ikikkikikkik*ikikik=ikikikkikk ikikkikikkik*ikikki=kkikikk ikikkikikkik*ikkiki=kikkik ikikkikikkik*ikkikk=kikkikik ikikkikikkik*kikikk=kikikki ikikkikikkik*kikkik=ikkiki ikikkikikkik*kkikik=ikikkikk ikikkikikkik*ikikikk=ikikikki ikikkikikkik*ikikkik=kkiki ikikkikikkik*ikkikik=kikkikk ikikkikikkik*kikikki=kikikk ikikkikikkik*kikkiki=ikkik ikikkikikkik*kikkikk=ikkikik ikikkikikkik*kkikikk=ikikki ikikkikikkik*ikikikki=ikikikk ikikkikikkik*ikikkiki=kkik ikikkikikkik*ikikkikk=kkikik ikikkikikkik*ikkikikk=kikki ikikkikikkik*kikikkik=kiki ikikkikikkik*kikkikik=ikkikk ikikkikikkik*kkikikki=ikikk ikikkikikkik*ikikikkik=ikiki ikikkikikkik*ikikkikik=kkikk ikikkikikkik*ikkikikki=kikk ikikkikikkik*kikikkiki=kik ikikkikikkik*kikikkikk=kikik ikikkikikkik*kikkikikk=ikki ikikkikikkik*kkikikkik=iki ikikkikikkik*ikikikkiki=ikik ikikkikikkik*ikikikkikk=ikikik ikikkikikkik*ikikkikikk=kki ikikkikikkik*ikkikikkik=ki ikikkikikkik*kikkikikki=ikk ikikkikikkik*kkikikkiki=ik ikikkikikkik*ikikkikikki=kk ikikkikikkik*ikkikikkiki=k ikikkikikkik*kikkikikkik=i ikikkikikkik*ikikkikikkik=1

by Ken (noreply@blogger.com) at July 03, 2017 08:21 PM

June 30, 2017

Functional Jobs

OCaml server-side developer at Ahrefs (Full-time)

What we need

Ahrefs is looking for a backend developer with a deep understanding of networks, distributed systems, OS fundamentals and taste for simple and efficient architectural designs. Our backend is implemented mostly in OCaml and some C++, as such proficiency in OCaml is very much appreciated, otherwise a strong inclination to intensively learn OCaml in a short term will be required. Understanding of functional programming in general and/or experience with other FP languages (F#,Haskell,Scala,Scheme,etc) will help a lot. Knowledge of C++ and/or Rust is a plus.

Every day the candidate will have to deal with:

  • 10+ petabytes of live data
  • OCaml
  • linux
  • git

The ideal candidate is expected to:

  • Independently deal with bugs, schedule tasks and investigate code
  • Make argumented technical choice and take responsibility for it
  • Understand the whole technology stack at all levels : from network and userspace code to OS internals and hardware
  • Handle full development cycle of a single component - i.e. formalize task, write code and tests, setup and support production (devops), resolve user requests
  • Approach problems with practical mindset and suppress perfectionism when time is a priority
  • Write flexible maintainable code and adapt to post-launch requirements’ tweaks

These requirements stem naturally from our approach to development with fast feedback cycle, highly-focused personal areas of responsibility and strong tendency to vertical component splitting.

Who we are

Ahrefs runs an internet-scale bot that crawls the whole Web 24/7, storing huge volumes of information to be indexed and structured in a timely fashion. Backend system is powered by a custom petabyte-scale distributed key-value storage to accommodate all that data coming in at high speed. The storage system is implemented in OCaml with thin performance-critical low-level part in C++. On top of that Ahrefs is building various analytical services for end-users.

We are a small team and strongly believe in better technology leading to better solutions for real-world problems. We worship functional languages and static typing, extensively employ code generation and meta-programming, value code clarity and predictability, and are constantly seeking to automate repetitive tasks and eliminate boilerplate, guided by DRY and following KISS. If there is any new technology that will make our life easier - no doubt, we'll give it a try. We rely heavily on opensource code (as the only viable way to build maintainable system) and contribute back, see e.g. https://github.com/ahrefs . It goes without saying that our team is all passionate and experienced OCaml programmers, ready to lend a hand and explain that intricate ocamlbuild rule or track a CPU bug.

Our motto is "first do it, then do it right, then do it better".

What you get

We provide:

  • Competitive salary
  • Informal and thriving atmosphere
  • First-class workplace equipment (hardware, tools)
  • Medical insurance

Locations

Singapore : modern office in CBD

USA : cozy loft in San Francisco downtown

Get information on how to apply for this position.

June 30, 2017 07:59 PM

June 25, 2017

Philip Wadler

PLDI and PACMPL - have your say!


Proceedings of the ACM on Programming Languages (PACMPL) is a new, open-access journal that will archive the results of major programming language conferences sponsored by SIGPLAN and ACM. So far, ICFP, OOPSLA, and POPL have signed on. There is, to my surprise, a raging debate as to whether PLDI should do so. The issues are blogged here, and there is a survey here.

As Editor-in-Chief of PACMPL, I may be prejudiced, but it seems to me the case for PLDI to join is a no-brainer.  Programming languages are unusual in a heavy reliance on conferences over journals. In many universities and to many national funding bodies, journal publications are the only ones that count. Other fields within computing are sorting this out by moving to journals; we should too. Journals cover a wide range of different publications, and our better conferences sit toward the high-quality end of this range. ICFP, OOPSLA, and POPL were all enthusiastic to join; is PLDI that different?

Becoming a journal requires a slight change to procedure: an extra round for referees to ensure necessary changes have been made. The extra round increases reliability of our archival publication—good, as we don't want to build our field on sand!—and may permit the PC to be more adventurous in accepting borderline papers.

Most importantly, all papers in PACMPL will be open access, thanks to generous underwriting by SIGPLAN. The price ACM is charging is too high, and we will continue to press them to reduce it. But it is only by going to open access that SIGPLAN can survive—the alternative is that our conferences, including PLDI, will wither, to be replaced by others that are open access.

I urge you to fill out the survey, as it is your opinion that could tilt the balance. Though the survey is non-binding, it will powerfully influence the PLDI Steering Committee when they vote on the issue next month. It just takes a minute, do it now!


by Philip Wadler (noreply@blogger.com) at June 25, 2017 01:42 PM

DSLDI 2017

DSLDI 2017, colocated with SPLASH in Vancouver, October 2017.
Please submit to
DSLDI is a single-day workshop and will consist of an invited speaker followed by moderated audience discussions structured around a series of short talks. The role of the talks is to facilitate interesting and substantive discussion. Therefore, we welcome and encourage talks that express strong opinions, describe open problems, propose new research directions, and report on early research in progress.
Proposed talks should be on topics within DSLDI’s area of interest, which include but are not limited to:
  • solicitation and representation of domain knowledge
  • DSL design principles and processes
  • DSL implementation techniques and language workbenches
  • domain-specific optimizations
  • human factors of DSLs
  • tool support for DSL users
  • community and educational support for DSL users
  • applications of DSLs to existing and emerging domains
  • studies of usability, performance, or other benefits of DSLs
  • experience reports of DSLs deployed in practice

by Philip Wadler (noreply@blogger.com) at June 25, 2017 12:59 PM

June 23, 2017

Joachim Breitner

The perils of live demonstrations

Yesterday, I was giving a talk at the The South SF Bay Haskell User Group about how implementing lock-step simulation is trivial in Haskell and how Chris Smith and me are using this to make CodeWorld even more attractive to students. I gave the talk before, at Compose::Conference in New York City earlier this year, so I felt well prepared. On the flight to the West Coast I slightly extended the slides, and as I was too cheap to buy in-flight WiFi, I tested them only locally.

So I arrived at the offices of Target1 in Sunnyvale, got on the WiFi, uploaded my slides, which are in fact one large interactive CodeWorld program, and tried to run it. But I got a type error…

Turns out that the API of CodeWorld was changed just the day before:

commit 054c811b494746ec7304c3d495675046727ab114
Author: Chris Smith <cdsmith@gmail.com>
Date:   Wed Jun 21 23:53:53 2017 +0000

    Change dilated to take one parameter.
    
    Function is nearly unused, so I'm not concerned about breakage.
    This new version better aligns with standard educational usage,
    in which "dilation" means uniform scaling.  Taken as a separate
    operation, it commutes with rotation, and preserves similarity
    of shapes, neither of which is true of scaling in general.

Ok, that was quick to fix, and the CodeWorld server started to compile my code, and compiled, and aborted. It turned out that my program, presumably the larges CodeWorld interaction out there, hit the time limit of the compiler.

Luckily, Chris Smith just arrived at the venue, and he emergency-bumped the compiler time limit. The program compiled and I could start my presentation.

Unfortunately, the biggest blunder was still awaiting for me. I came to the slide where two instances of pong are played over a simulated network, and my point was that the two instances are perfectly in sync. Unfortunately, they were not. I guess it did support my point that lock-step simulation can easily go wrong, but it really left me out in the rain there, and I could not explain it – I did not modify this code since New York, and there it worked flawless2. In the end, I could save my face a bit by running the real pong game against an attendee over the network, and no desynchronisation could be observed there.

Today I dug into it and it took me a while, and it turned out that the problem was not in CodeWorld, or the lock-step simulation code discussed in our paper about it, but in the code in my presentation that simulated the delayed network messages; in some instances it would deliver the UI events in different order to the two simulated players, and hence cause them do something different. Phew.


  1. Yes, the retail giant. Turns out that they have a small but enthusiastic Haskell-using group in their IT department.

  2. I hope the video is going to be online soon, then you can check for yourself.

by Joachim Breitner (mail@joachim-breitner.de) at June 23, 2017 11:54 PM

wren gayle romano

"Spring semester" in review

Hi all, long time no post. A lot has been going on, but I’m finally starting to get on top of things again. I’ve been meaning to write in a bit more depth about some of this, but that want for perfection has been the enemy of the writing anything at all. So, here’s a quick synopsis of what’s been going on in my neck of the woods.

Both of L’s parents passed away. We’ve known this was coming, but it’s still hard of course. L was out there for a bit over a month taking care of her mom. They died very close together, so we ended up having a single combined service. I was out there for about a week helping to wrap things up before whisking L back home.

I finally got back the results of the genetics test. Turns out I don’t have Loeys–Dietz, or at least not the same genetic variant my mother did. But I definitely have something. So it’s back to the diagnostic swamp trying to figure out how to give it a name so that doctors’ll take it seriously. Current working hypothesis is hypermobility-type Ehlers–Danlos. Alas, “hypermobility-type” is medical jargon for “we have no idea what this is, but it kinda looks similar to the forms of Ehlers–Danlos we do know stuff about, so let’s call it that.” So, yeah, no medical tests to “prove” that’s what it is; just your usual game of convincing folks you have enough of the symptoms to match the syndrome.

I’ve been getting used to paying attention to my ADHD and working with it rather than trying to plow through it. It helps a lot to recognize that it’s not a failing on my part (e.g., that I can’t focus on boring things for as long as other people) but rather just part of how I’m wired. That makes it a lot easier to stop beating myself up over things, and instead figure out better ways to work with my brain rather than trying to force it into a shape it won’t take. As I’ve gotten better at this I’ve finally started getting caught up on a bunch of things that’ve fallen to the wayside over the past few years.

For example, I’m slowly getting caught up on the backlog of bug reports and feature requests for my various Haskell packages. Mostly been focusing on logfloat and unification-fd so far, but will make it around to the others in time. So, if you sent me an email about some bug or feature over the past few years and it seems to have fallen into the void, consider filing a ticket.

Still working on getting caught up to where I should be on my dissertation.

Work has also been going excellently. It’s all seekrit and nonsense, so I can’t say too much about it. But lately I’ve been doing a bunch of work on characterizing families of mathematical objects, and discovering their symmetries so we can exploit them to simplify and optimize things. So lots of mathy goodness going on. It’s a bit more geometric and combinatorial than my usual algebraic fare, but it’s the sort of stuff that arises from algebraic structures so it’s not too far from home base. (If that doesn’t make sense to you, maybe take a look at Brent Yorgey’s thesis to see an example of the connection between combinatorics and algebraic data types.) Plus, it helps that I’ve been getting to know some of the hella queer ladies who work in my building :)

In other health-y news, round about the time I got officially diagnosed with ADHD I had a bunch of friends going on about what the symptoms of allism (aka non-autism) are. Though I have a bunch of autistic friends, I’ve never really known much about what autism’s really like because all the literature is written by allistic folks, for allistic folks, so they’re all “patient has underdeveloped/insufficient blah” and I’m like “according to what baseline? How much blah does it take to count as having ‘sufficient’ blah? What are diagnostic details for measuring how much blah you really have?” So I finally got to hear some details from the autistic side of the fence, where people actually explain shit and elucidate the differences. And based on that: I’m hella not allistic. I can (and should! and have been meaning to!) write a whole separate post on this topic. I’m still not entirely sure I feel comfortable adopting “autistic” label (for reasons which are, themselves, further symptoms of autism), because my experiences don’t match up perfectly with some of the parts of what is traditionally called “autism”, but I’m absolutely non-allistic. I think the spectrum of non-allism is far larger and more diverse than allistic people currently believe, but —again— a post for another time.



comment count unavailable comments

June 23, 2017 05:37 AM

June 22, 2017

Philip Wadler

RADICAL 2017


Please submit to RADICAL 2017, Recent Advances in Concurrency and Logic, a workshop co-located with QONFEST (CONCUR, QEST, FORMATS, and EPEW), Berlin (Germany), September 4, 2017.
As you know, submissions to RADICAL could be, for instance:- reports of an ongoing work and/or preliminary results;- summaries of an already published paper (even at CONCUR'17 - see below);- overviews of (recent) PhD theses;- descriptions of research projects and consortia;- manifestos, calls to action, personal views on current and future challenges;- overviews of interesting yet underrepresented problems.
...
Many thanks for your cooperation!Julian and Jorge

by Philip Wadler (noreply@blogger.com) at June 22, 2017 02:26 PM