00:00:16.080
Welcome back to the Mountain West Functional Programming Conference.
00:00:25.279
My name is James Britt, and I’m going to talk to you about a project called Hubris, which acts as a bridge between Ruby and Haskell.
00:00:32.480
Can you hear me okay? I’ll try to speak up and talk into the microphone instead of leaning into the laptop.
00:00:38.719
I’ve been a Ruby programmer since the turn of the century. You’re better off just Googling my name, so we can skip the introductions.
00:00:50.079
I want to cover two things: why Haskell, and what Hubris is about.
00:01:02.320
First, I’m not particularly well-versed in Haskell. I’ve been learning more about it, and my interest in Haskell is what led me to Hubris.
00:01:08.880
A lot of what I’ll share is based on enthusiasm for certain ideas and expectations, even though I’m not a Haskell expert.
00:01:18.960
It occurred to me that I would be trying to convince you all that Haskell is worth your interest, despite my limited knowledge.
00:01:25.200
But the key takeaway is this: whether you care about Haskell or not, it’s important to stimulate your mind and pursue interesting ideas.
00:01:31.040
One of the things I enjoy about Mountain West RubyConf is the crowd. The people here are software developers who are genuinely interested in coding and learning.
00:01:38.880
So, I hope what I discuss resonates with you. Now, how many of you have heard of Haskell?
00:01:44.640
Great! And how many of you code in Haskell regularly? That may be a good thing, as it means you won’t be shouting at me if I make mistakes.
00:01:51.040
Haskell is a functional programming language named after the mathematician Haskell Curry.
00:01:57.200
It has a reputation for being an academic language that is somewhat arcane, often thought to be incapable of practical application because of its disallowance of side effects or state. However, that’s not accurate.
00:02:08.720
What I find intriguing about Haskell is that it is a purely functional programming language that utilizes type inference.
00:02:14.959
It features static typing but doesn’t require excessive boilerplate code to define types.
00:02:21.040
In many cases, the types can be inferred automatically, although there are compelling arguments for using explicit typing for safety in your code.
00:02:27.840
Another interesting aspect of Haskell is that it embodies referential transparency, which essentially means variables are immutable.
00:02:34.560
Once a variable is set, it retains its value. This results in predictable behavior in code since calling a function with the same parameters always yields the same result.
00:02:41.440
Those familiar with unit testing and mocking understand how significant this is; it represents an ideal state that is often hard to achieve.
00:02:47.760
A language that enforces this kind of behavior is a fascinating concept to me.
00:02:52.879
Often, when discussing functional programming, people lump in languages like JavaScript or Python, claiming they support functional programming.
00:03:05.200
That’s misleading; they allow functional programming patterns to some extent, but the absence of referential transparency and other fundamental aspects means they can't fully embody functional programming.
00:03:13.040
When I got interested in Haskell around two years ago, I wanted to explore a new kind of language.
00:03:19.840
I was already somewhat familiar with Lisp and had been hearing more about functional programming languages.
00:03:27.120
When I looked at languages categorized as functional, I was drawn to Haskell because it seemed the most rigorous.
00:03:35.040
Learning Haskell has been like participating in the X Games of programming; it’s quite demanding about what it expects from you.
00:03:44.640
While some other languages allow a procedural approach, Haskell tends to guide you toward functional programming.
00:03:51.360
In truth, you still have the option to employ procedural programming in Haskell, but it’s less likely you’ll be inclined to do so.
00:03:57.680
Another way to view Haskell is as a fundamentally functional programming language, firmly rooted in lambda calculus.
00:04:05.120
It boasts a strong mathematical foundation and allows for secure reasoning about the code you write.
00:04:13.040
One of the driving reasons I became interested in Hubris was that I found it challenging to access good resources to help me learn Haskell.
00:04:21.920
My approach to learning programming, which I also applied to Ruby, involved writing small, useful applications.
00:04:30.720
I had previously done a lot of programming with Perl, but when I became interested in Ruby, I started writing in Ruby.
00:04:38.160
I found building skills in Ruby was much easier, yet transitioning to Haskell posed significant challenges.
00:04:46.560
Even simple tasks, such as reading command line arguments or files, were less intuitive for me given my background.
00:04:55.200
I suspect many people may find the Haskell paradigm perfectly clear, particularly if they come from a Lisp background.
00:05:02.400
Hubris serves as a bridge between Ruby and Haskell, allowing you to call Haskell code from Ruby.
00:05:08.000
The project was initiated by Mark Wattenberg, so credit goes to him for the useful features of Hubris.
00:05:17.440
I joined the project not too long ago to help out with the Ruby side, and I’ve recently become a committer.
00:05:24.800
Involvement in the project led me to realize that being a committer could be a great way to learn Haskell.
00:05:32.080
Let’s address why one might choose to use a language like Haskell.
00:05:39.120
One compelling reason is that Haskell code can run faster since it compiles to native code.
00:05:45.680
Another reason is that using Hubris is a way to learn Haskell.
00:05:53.680
You can write applications whose awkward parts, such as I/O, can be done in Ruby.
00:06:02.720
Once you’ve done that, you can write business logic in Haskell and expand your use of Haskell as you become more comfortable.
00:06:10.160
Moreover, coding in Haskell allows you to impress people on Reddit by sharing your Haskell stories.
00:06:17.760
When discussing the speed of code, it's useful to quantify just how fast Haskell is.
00:06:25.560
I planned to provide a complex demo but found it more challenging than expected.
00:06:36.080
However, I discovered an example of code that generates the Mandelbrot set, both in Ruby and Haskell.
00:06:43.520
I didn't document the author's name in my slides, but I believe it is Saba.
00:06:51.840
I decided to use that code for my demonstration.
00:06:58.960
I assume everyone knows what the Mandelbrot set is, right?
00:07:07.680
Essentially, it’s a visualization of an equation applied across the complex plane, resulting in patterns that can be stunning.
00:07:13.200
The equation is simple, which makes it computationally intensive.
00:07:20.480
Let me run the Ruby version of the Mandelbrot code.
00:07:27.600
I wrapped it in a shell script that measures the time it takes to execute, which is the key aspect here.
00:07:35.040
As it loops through, it prints an asterisk for each point calculated.
00:07:43.040
With 500 iterations for each point, it checks if something should be drawn.
00:07:50.000
That felt quite slow; it took around 20 seconds to execute.
00:07:57.440
Now let's run the Haskell version. Haskell can be compiled and run like a script.
00:08:09.760
I’ll compile and execute the Haskell code now.
00:08:16.640
Some of the time taken is due to compiling the code, but it finished much faster.
00:08:23.760
The Haskell version took around 2.5 to 3 seconds.
00:08:29.520
To be fair, in Ruby, discussions about speed are frequent, with a perpetual search for ways to improve performance.
00:08:37.680
Often, the received answer is simply to use a faster language.
00:08:44.880
However, Haskell may provide real advantages, especially for computationally intensive tasks.
00:08:51.920
Let’s see how the Haskell code performs compared to Ruby.
00:08:59.440
The Mandelbrot example in C is extremely fast, taking almost no time at all, as the output appears to produce an image instantly.
00:09:06.720
I’ll note that I’m not claiming this is optimized Haskell; there are likely many who could squeeze even more performance from it.
00:09:12.960
That said, performance isn’t the only reason for choosing a language.
00:09:20.080
Haskell offers powerful abstractions that can be beneficial for certain problem domains.
00:09:29.760
For me, choosing a language comes down to how it enables me to think about and reason through problems.
00:09:36.000
Furthermore, Haskell has support for parallel execution, a significant advantage that can be challenging to achieve independently.
00:09:43.760
Hubris serves not only as a technical bridge between Ruby and Haskell, but also as a conceptual bridge, introducing various ways of thinking about coding.
00:09:51.680
Initially, while preparing my slides, I wanted to showcase a clever example.
00:09:57.680
I found an interesting tutorial on the Lisparati site, which has a good introduction on both Lisp and Haskell.
00:10:05.760
The tutorial features a fun example with picnics and robots, which is engaging.
00:10:12.160
I thought I would translate the Haskell code into Ruby, using that as an example of Hubris.
00:10:19.360
However, translating it proved to be more challenging than expected.
00:10:30.320
One significant learning from this process was that I gained a better understanding of Haskell by translating from Haskell to Ruby.
00:10:40.320
If I had tried to convert Ruby to Haskell, I likely wouldn’t have grasped the idiomatic Haskell concepts as well.
00:10:47.840
It’s often easier to take idiomatic Haskell and translate it than to try to force identification of Ruby idioms in a new language.
00:10:56.320
This process helped me to appreciate the parallels between Haskell’s features and Ruby’s capabilities.
00:11:04.640
Many aspects of Haskell are appealing to those who appreciate the flexibility of Ruby, such as function passing and composition.
00:11:14.720
While attempting to translate code, I encountered some trivial parts that were straightforward in Haskell but quite difficult in Ruby.
00:11:23.200
This led me to consider whether Haskell might be a better version of Ruby, or at least an acceptable alternative for those who enjoy certain Ruby features.
00:11:30.080
I think Haskell can indeed serve that role.
00:11:39.840
While my experience with Haskell is somewhat limited, this exploration continues to motivate me to learn more about the language.
00:11:48.880
Learning Haskell has made me a better Ruby developer and I encourage other Rubyists to pursue experience with functional programming languages.
00:11:54.920
It’s valuable to explore other languages to hone your skills.
00:12:01.760
Next, I will provide some code examples, specifically focusing on Hubris, which involves Haskell code.
00:12:10.080
Just a reminder: Haskell is a language without side effects.
00:12:16.640
Now, let’s look at some examples of how this works.
00:12:25.840
Regarding the Mandelbrot example, there are multiple ways you can use Hubris.
00:12:39.360
The Hubris project is available on GitHub under Mark Watten's profile.
00:12:47.920
The repo includes examples, which I hope to expand upon, demonstrating how to call Haskell code inline.
00:12:56.320
You can also load existing Haskell library modules.
00:13:02.560
First, let me demonstrate some inline Haskell.
00:13:12.240
I want to make sure you can read the example code. Should I adjust the size or color scheme?
00:13:20.640
For this example, I will use a basic Ruby script.
00:13:27.760
I’ll begin by requiring the local copy of Hubris.
00:13:34.880
Here, I define a class and how to add a method defined inline.
00:13:41.200
This part of the code shows a type declaration for a Haskell function called triple, which takes an integer and returns an integer.
00:13:48.800
The definition states that triple of n equals three times n, which is relatively straightforward.
00:13:56.240
Haskell relies on indentation to indicate scope. However, you can use curly braces and colons to manage longer definitions.
00:14:04.160
Once defined, we can create an instance of the target object and call that method.
00:14:10.080
This underlying mechanics cause Hubris to execute Haskell code by loading it as a shared object.
00:14:18.240
If calling inline Haskell code, Hubris first writes it out to a file, wraps it as a Haskell module, compiles it, and then binds it to Ruby as a shared object.
00:14:25.760
This process, though reliant on some code generation, is quite powerful but can take several seconds.
00:14:31.600
I apologize for the slowdown in the example.
00:14:39.440
Usually, a quick demonstration with Haskell is not as cumbersome.
00:14:45.760
The code you define inline gets placed into a module that Hubris automatically generates.
00:14:53.520
Let’s try defining an external file now.
00:14:59.680
I created a simple web application to use the Mandelbrot code and render it as a webpage.
00:15:06.560
While dealing with Hubris for various reasons related to speed, I learned that when passing strings to Haskell through Hubris, those strings are treated as byte strings.
00:15:12.960
This means you might have to do some manipulation to get the correct string values back.
00:15:19.520
I ended up populating the output with integers and converting them to spaces and asterisks in Ruby.
00:15:27.200
Here’s the controller code that runs the Hubris operations.
00:15:34.080
I added an option called 'no strict' while compiling the Haskell code, as some warnings were causing issues.
00:15:41.680
By default, Hubris sets strict compiler flags, which may not be ideal in every case.
00:15:48.960
It’s crucial to point the source to the Haskell directory to allow the application to find the Haskell code.
00:15:54.560
The index of the web application will pass in a default value of 500 for the number of iterations.
00:16:02.160
I also set up basic timing functionality in this code.
00:16:09.440
Now, let’s take a look at the Haskell code.
00:16:15.760
This Haskell code imports all necessary libraries and defines the module explicitly.
00:16:23.520
According to Hubris, you must specify the module for external files and define the functions accessible from Ruby.
00:16:30.960
I had to ensure the relevant functions were exposed properly; otherwise, Hubris would complain.
00:16:39.200
This Haskell code implements the core functionality of the Mandelbrot set.
00:16:49.360
I’ll explain the code functionality and emphasize an important point about type decoration.
00:16:57.440
The type signature is omitted, but if the compiler cannot infer the type based on code, it will fail to build.
00:17:05.760
However, it’s essential to provide type signatures to aid Hubris with strong typing.
00:17:12.800
This helps the compiler assess whether the code works correctly.
00:17:21.040
The first section here involves pushing properties onto the x and y axes.
00:17:29.440
This concept parallels a step function in Ruby, iterating over x to iterate over y.
00:17:36.960
The function analyzes whether the magnitude of the result from calling mandelbrot is less than two.
00:17:45.440
If so, it appends a 1 to the result; otherwise, it appends a 0.
00:17:52.960
This function takes an expression and generates an infinite list of values, exemplifying lazy evaluation.
00:17:59.440
In practice, it operates only as needed, following the request to evaluate.
00:18:06.960
This technique allows extracting specific iterations based on the given parameters.
00:18:12.800
The rendering time using the web browser with 500 iterations is under 2 seconds.
00:18:20.080
Suppose I set the number of iterations to around 1200. In that case, the performance remains impressive.
00:18:30.560
Let’s compare that to our earlier Ruby execution of the Mandelbrot, keeping in mind the performance.
00:18:37.760
The Ruby implementation, while functional, took about 21 seconds to complete the same task.
00:18:45.120
The speed difference highlights possible advantages in using Haskell for computationally intensive tasks.
00:18:56.160
If you’re interested in building with Hubris, you'll need GHC version 6.12, the Glasgow Haskell Compiler.
00:19:02.960
The Haskell community has actively worked on making it accessible and easy to get started with Haskell.
00:19:11.200
They recently released the Haskell Platform, which contains essential libraries, but it currently packages GHC 6.10.
00:19:19.760
The upcoming Haskell platform release will include GHC 6.12, which is exciting.
00:19:27.280
For installation, I recommend checking the GitHub wiki for instructions, as getting dependencies right is key.
00:19:34.080
Be mindful, as installation can be cumbersome; dependencies need to align precisely.
00:19:43.840
Also, Hubris currently faces limitations with 64-bit operating systems, which I discovered while trying to install it.
00:19:52.320
Temporary glitches exist, yet the Haskell community is eager to enhance usability.
00:20:01.920
I’ve been running Haskell through a virtual machine to ensure all dependencies can be resolved.
00:20:09.360
Haskell works with Hubris in such a way that it compiles code to shared objects, which Ruby can access as shared libraries.
00:20:16.480
Cabal is an outstanding dependency management tool in the Haskell ecosystem, comparable to Ruby gems.
00:20:23.840
It works great, and you should definitely install it for your Haskell projects.
00:20:29.920
But, careful configuration is essential; failure to set the enable-shared flag could lead to significant issues.
00:20:36.960
In summary, Hubris exposes limitations related to functions only taking one argument, which is a design choice to simplify typing and function management.
00:20:45.760
Executing calls can be cumbersome, especially when it comes to Ruby strings, as they convert to Haskell byte strings.
00:20:55.040
Handling string inputs and outputs can become tricky, often requiring additional libraries for conversion.
00:21:03.760
I am nearing the end of my time, are there any questions?
00:21:10.480
Yes, you can pass Ruby objects; this is a good question.
00:21:17.760
However, I haven't explored everything in that regard yet.
00:21:23.760
I believe it's treated as a broad type, but I will confirm.
00:21:32.240
Any other questions?
00:21:39.760
What about function passing? Yes, that’s a fascinating topic.
00:21:46.560
I'm unsure about that, and I'll need to investigate further.
00:21:52.560
Have you tried Cabal? Yes, I have experience using it and find it an excellent tool.
00:21:58.880
It’s the Haskell equivalent of RubyGems, and the experience improves with time.
00:22:05.920
Careful about proper settings, as mismatches can create operational difficulties.
00:22:13.120
One last question—how did you create the visuals in your presentation?
00:22:19.680
I made those graphics in Illustrator.
00:22:26.560
Thank you all very much for your time!