未加星标

Using JavaScript to Learn Haskell

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二05 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

I remember when I first learned of the existence of Haskell . It was actually around the time that Apple first released Swift . Excited by the prospect of learning an entirely new language and participating in an entirely new language community, I dove right in ― and immediately slammed into the hard concrete of Optionals . I was suddenly confronted with implications of type safety I had never thought about before. What should happen if you try to access a value that does not exist? Should the compiler give you an error? A warning? Should your program compile but throw an exception at runtime? Is it the responsibility of the programmer to handle errors or the compiler to refuse to emit unsafe code? One question that had not occurred to me was whether the designers of a language themselves should do something syntacticly to prevent errors. In Swift, optionals were the proposed solution to the conundrum of handling values that might not exist. When accessing a value from a dictionary, for example, Swift does not return the value for a given key but an optional value that includes, as part of the value’s type, the possibility that the value does not actually exist. In order to access the raw value, you have to either forcibly “unwrap” it or use an if let expression to test for the possibility of nil:

Words can hardly express, for the newcomer to Swift, just how annoying this was. In earlier versions of the language, you could rapidly end up with if let pyramids of doom if you had multiple optionals to unwrap. This problem was mitigated somewhat in Swift 1.2 and finally addressed head-on with the addition of the guard statement to Swift 2.0. But by that point, I had already moved away from Swift. Wondering where concepts like optionals came from, I early-on discovered something called “functional programming” and a language called “Haskell” that everybody said is “hard”. Since I am always interested in getting closer to the source of ideas, and rather stubborn about learning things people claim to be difficult, I thought it could only enhance my understanding of Swift if I learned Haskell, too. What I did not expect was to be drawn into the FP world so completely that I lost interest in Swift entirely.

It’s not that Swift isn’t an interesting language. I definitely prefer it to Objective-C, which I never managed to learn in-depth (I blame my early exposure to C++). It’s just that after encountering Haskell and functional programming in its natural habitat, I found Swift to be ultimately much less exciting than I had thought it would, could, or should be. The way I see things, Swift is trying too hard to have it both ways: it combines the type safety of a language like Haskell with the syntactic pizazz of dynamic scripting languages such as python and Ruby. This latter quality, I believe, is what leads people to refer, incongruously, to certain programming languages as “fun”. As far as language evolution is concerned, Swift is also just another iteration of the ALGOL/C family. Will it also mimic the tendency of C++ to pack in as many features as possible, whether or not they contradict one another? Sure, it supports Unicode, so you can assign values to unicorns and piles of poop, but it does not otherwise seem like a significant advancement to me.

Learning Haskell, on the other hand, was like entering a portal to another universe, one with entirely different laws of physics. Suddenly, I couldn’t reassign variables (what?); or create objects to represent my data (What?); or even perform basic I/O operations, like printing to the screen, in an obvious and straightforward manner (WHAT?). What I could do, however, is write the most elegant, concise, and correct code of my life. Sure, it all looks like algebraic alphabet soup at first. But once you get past the general lack of curly braces and parentheses, once you learn to think in terms of “why” instead of “how” to do things, once you accept the infelicity of languages that model the operations of the machine instead of the operations of problem solving, you begin to embrace the virtues of things like functional purity, composition, and referential transparency and cease to fear the abstractions represented by abstruse terms like functor and monad.

As a means to overcome the hurdles to learning Haskell, which are real enough, I decided to re-implement a selection of its functions in javascript. With the ES2015 additions to the language, which I also wanted to learn, this seemed like it would be a more attainable goal that I could accomplish with less syntactic cruft than would have been possible before. Moreover, I was already familiar with JavaScript (unlike Swift), and, since it has higher-order functions and a rather relaxed approach to type safety, I thought I would be able to interpret Haskell code faithfully enough and without having to satisfy the demands of a language (like Swift) with a more cantankerous compiler. To put it another way, JavaScript’s weaknesses as a language are what made it an ideal environment in which to experiment with what I found to be a stronger programming paradigm.

I quickly realized, on beginning this ambitious project, that I could not simply jump in and start writing interesting functions right away. If I wanted my JavaScript code to even approach the elegance of Haskell, I had some plumbing to do first. Haskell functions are automatically curried and are, partly for that reason, composable. In order to establish the same foundation for my code, I started this adventure by writing two utility functions, partial and $ . The partial function acts as a kind of currying API by abstracting away the boilerplate code that would otherwise be required to make partial application possible for a JavaScript function:

This function works by taking its parameters ― a function followed by all the arguments that function expects ― and recursively applying the function to each argument until the function is either fully applied or it runs out of arguments. In the former case, partial simply calls the function and returns whatever value the fully-applied function evaluates to. In the latter case, partial returns a new function partially applied to the given arguments and ready to accept further arguments until it is fully applied.

The $ function works like the Haskell composition operator:

All this function (in the guise of an infix operator) does is take two functions, f and g and compose them, which means returning a new function that applies f to the result of g applied to the argument x . Here’s my JavaScript version:

Since the dot is already spoken for in JavaScript, I chose to represent my own, comparable operation with the bling ― it is, at least, a nod to Haskell’s own right-associative binding operator, also $ . Note that it works similarly to partial insofar as, in the absence of an x argument, it returns a new function that expects one and, given an x , it simply evaluates the composition of f and g . Also: one-liner!

With this groundwork laid, I was able to begin construction of the rest of the project’s edifice. I want to present here just a few, very basic examples of what is possible with functional programming by showing some easy to read Haskell functions along with my own JavaScript versions. In Haskell, boolean operators are just functions like any others. They may be defined as infix functions for the sake of clarity (and consistency with other languages), but this is only syntactic sugar, and it isn’t even mandatory. This is how the basic boolean operators AND, OR, and NOT are defined in Haskell:

The first line of each function is called a “function signature.” The signature expresses the types of the function’s parameters and return values. The arrows indicate function applications. For the sake of simplicity, you could regard the final type as the actual return type of the function and the others as arguments. For example, && takes two arguments, a boolean and another boolean, and returns a boolean. That part should be straightforward. What is actually happening, however, is that && takes a single boolean argument and returns a new function that is partially applied to that argument. This new function also takes a single boolean argument. When it is applied to that argument, the function as a whole is then fully applied, and the result reduces to a boolean value, True or False . The body of the function performs pattern matching to determine this final value. If the first argument is True , the final value will be the same as the value of x . If the first argument is False , however, then it doesn’t matter what the second argument is, because the function will always evaluate to False in that case. The underscore indicates that the second value does not matter. The patterns in the other functions ought to be self-explanatory along these lines. No need for extra syntax, like if let expressions and guard keywords here!

My JavaScript versions are not quite as easy on the eyes, but they have the same meaning. Unfortunately, without Haskell’s built-in currying and type checking, I could not avoid exposing some of the plumbing I installed to replicate them:

The important parts of these functions, two lines in each one, are identical to their Haskell counterparts. The bulk of them, however, are given over to type checking (which I arguably could have left out of these examples) and the affordance for partial application. In the future, I may want to replace the rather clunky type checking with something invisible, built using the new Proxy and Symbol APIs. I may even be able to do the same for partial application, trapping function calls with Proxy and doing all the Haskell-y stuff behind the scenes. In the meantime, I think it’s salutary to see how these things can be implemented with less complicated code as well as the challenges of adapting a strongly-typed paradigm to a weakly-typed language.

For my final two examples, I will show off partial application and function composition together. Haskell defines two functions, even and odd in terms of one another:

Each function takes a value of type a , which must be some kind of integer, and returns a boolean. Note again the patterns: if n is an even integer, then even n returns True . In this function, you can also see the remainder or modulo function rem used as an infix operator, which is what the backticks signify. For odd , you could obviously write something like n `rem` 2 > 0 , but instead even is composed with the not function from above. It seems like such a small fragment of code, but why not reuse it? If a value is even, then it is not odd. And vice-versa. This sort of code reuse enabled by composition is fundamental to the functional programming philosophy, even if the size of the code seems trivial. Ideally, most functions should be as small as possible. To build up larger operations, piece the smaller ones together. Now, let’s see what this looks like in JavaScript, as written by me:

Although I had to implement them as two, separate functions, the meaning again remains the same. Note that my odd function uses the composition function, $ , I defined above. Note also the use of partial application. The $ function takes not and returns a new function that applies not to even , which also returns a new function that expects a value, a . If you’re used to function arguments all living happily together in comma-separated lists inside of parentheses, this format will no doubt strike you as strange. But it works to transform the higher-order functions of JavaScript into even more powerful curried functions, which are as easy to pass around as raw values themselves. What you might also notice, however, is that these two functions are also fundamentally unlike their Haskell equivalents. In fact, they are fundamentally broken. See if you can figure out why, and then consider the implications for writing clear, concise, pure functional code in a language that is not designed to support it.

If you found this brief excursion into Haskell-style JavaScript intriguing, please check out my complete library of Haskell functions translated into JavaScript, maryamyriameliamurphies . They’re fully-documented and might even turn out to be useful to you, whether as pedagogical aids or models for functional production code.

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

主题: JavaJavaScriptSwiftC++AppleObjective-CRubyWordPythonLG
分页:12
转载请注明
本文标题:Using JavaScript to Learn Haskell
本站链接:http://www.codesec.net/view/481276.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(28)