Talks
Hubris: The Ruby/Haskell Bridge

Hubris: The Ruby/Haskell Bridge

by James Britt

The video titled Hubris: The Ruby/Haskell Bridge features James Britt presenting at the MountainWest RubyConf 2010. In this talk, Britt discusses a project named Hubris, designed to serve as a bridge between the programming languages Ruby and Haskell.

Key Points Discussed:

  • Introduction to Haskell:
    • Haskell is a purely functional programming language known for its lack of side effects and its use of type inference. Britt emphasizes its academic reputation, noting it is capable of practical applications despite common misconceptions.
    • Key features include referential transparency, immutability, and static typing, which supports predictable code behavior, making it an appealing choice for developers.
  • Personal Journey with Haskell:
    • Britt shares his own limited experience with Haskell and how it fascinates him despite the challenges. He points out that Hubris was born out of frustration over limited resources for learning Haskell.
    • His approach to learning involved creating small applications, paralleling his experience with Ruby and Perl.
  • The Hubris Project:
    • Hubris enables developers to integrate Haskell into Ruby applications by calling Haskell code directly. The project has features contributed by others, including Mark Wattenberg.
  • Performance Comparison:
    • Britt presents a comparative demonstration using the Mandelbrot set generation in both Ruby and Haskell, highlighting notable differences in execution time. Haskell processes the task significantly faster—taking around 2.5 to 3 seconds compared to 20 seconds for Ruby.
  • Learning Haskell through Hubris:
    • He suggests that using Hubris provides a gentle introduction to Haskell while alleviating some complexities by allowing developers to continue utilizing Ruby for less intricate components like I/O.
  • Technical Aspects of Hubris:
    • Britt walks through both inline and external Haskell code examples, demonstrating how Hubris operates. He highlights challenges related to handling Ruby strings in Haskell and discusses project limitations, such as function parameter restrictions and operating system compatibility issues.
  • Encouragement for Ruby Developers:
    • Britt advocates for Ruby developers to learn functional programming through languages like Haskell, believing it can enhance their overall developing skills.

Conclusion:

The session closes with Britt encouraging the audience to explore Haskell as a way to expand their programming capabilities. He affirms that interacting with different languages, especially functional ones like Haskell, can significantly enrich a developer's toolkit.

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!