copy-edit viget posts

This commit is contained in:
David Eisinger
2023-10-24 20:48:09 -04:00
parent 0438a6d828
commit f86f391e82
77 changed files with 1663 additions and 1380 deletions

View File

@@ -2,7 +2,6 @@
title: "Functional Programming in Ruby with Contracts"
date: 2015-03-31T00:00:00+00:00
draft: false
needs_review: true
canonical_url: https://www.viget.com/articles/functional-programming-in-ruby-with-contracts/
---
@@ -15,48 +14,50 @@ docs](http://egonschiele.github.io/contracts.ruby/), I couldn't wait to
try it out. I'd been doing some functional programming as part of our
ongoing programming challenge series, and saw an opportunity to use
Contracts to rewrite my Ruby solution to the [One-Time
Pad](https://viget.com/extend/otp-a-language-agnostic-programming-challenge)
Pad](/elsewhere/otp-a-language-agnostic-programming-challenge)
problem. Check out my [rewritten `encrypt`
program](https://github.com/vigetlabs/otp/blob/master/languages/Ruby/encrypt):
#!/usr/bin/env ruby
```ruby
#!/usr/bin/env ruby
require "contracts"
include Contracts
require "contracts"
include Contracts
Char = -> (c) { c.is_a?(String) && c.length == 1 }
Cycle = Enumerator::Lazy
Char = -> (c) { c.is_a?(String) && c.length == 1 }
Cycle = Enumerator::Lazy
Contract [Char, Char] => Num
def int_of_hex_chars(chars)
chars.join.to_i(16)
end
Contract [Char, Char] => Num
def int_of_hex_chars(chars)
chars.join.to_i(16)
end
Contract ArrayOf[Num] => String
def hex_string_of_ints(nums)
nums.map { |n| n.to_s(16) }.join
end
Contract ArrayOf[Num] => String
def hex_string_of_ints(nums)
nums.map { |n| n.to_s(16) }.join
end
Contract Cycle => Num
def get_mask(key)
int_of_hex_chars key.first(2)
end
Contract Cycle => Num
def get_mask(key)
int_of_hex_chars key.first(2)
end
Contract [], Cycle => []
def encrypt(plaintext, key)
[]
end
Contract [], Cycle => []
def encrypt(plaintext, key)
[]
end
Contract ArrayOf[Char], Cycle => ArrayOf[Num]
def encrypt(plaintext, key)
char = plaintext.first.ord ^ get_mask(key)
[char] + encrypt(plaintext.drop(1), key.drop(2))
end
Contract ArrayOf[Char], Cycle => ArrayOf[Num]
def encrypt(plaintext, key)
char = plaintext.first.ord ^ get_mask(key)
[char] + encrypt(plaintext.drop(1), key.drop(2))
end
plaintext = STDIN.read.chars
key = ARGV.last.chars.cycle.lazy
plaintext = STDIN.read.chars
key = ARGV.last.chars.cycle.lazy
print hex_string_of_ints(encrypt(plaintext, key))
print hex_string_of_ints(encrypt(plaintext, key))
```
Pretty cool, yeah? Compare with this [Haskell
solution](https://github.com/vigetlabs/otp/blob/master/languages/Haskell/encrypt.hs).
@@ -69,7 +70,7 @@ output. Give it the expected classes of the arguments and the return
value, and you'll get a nicely formatted error message if the function
is called with something else, or returns something else.
### Custom types with lambdas {#customtypeswithlambdas}
### Custom types with lambdas
Ruby has no concept of a single character data type -- running
`"string".chars` returns an array of single-character strings. We can
@@ -81,14 +82,14 @@ says that the argument must be a string and must have a length of one.
If you're expecting an array of a specific length and type, you can
specify it, as I've done on line #9.
### Pattern matching {#patternmatching}
### Pattern matching
Rather than one `encrypt` method with a conditional to see if the list
is empty, we define the method twice: once for the base case (line #24)
and once for the recursive case (line #29). This keeps our functions
concise and allows us to do case-specific typechecking on the output.
### No unexpected `nil` {#nounexpectednil}
### No unexpected `nil`
There's nothing worse than `undefined method 'foo' for nil:NilClass`,
except maybe littering your methods with presence checks. Using
@@ -96,7 +97,7 @@ Contracts, you can be sure that your functions aren't being called with
`nil`. If it happens that `nil` is an acceptable input to your function,
use `Maybe[Type]` à la Haskell.
### Lazy, circular lists {#lazycircularlists}
### Lazy, circular lists
Unrelated to Contracts, but similarly inspired by *My Weird Ruby*, check
out the rotating encryption key made with
@@ -105,7 +106,7 @@ and
[`lazy`](http://ruby-doc.org/core-2.1.0/Enumerable.html#method-i-lazy)
on line #36.
\* \* \*
***
As a professional Ruby developer with an interest in strongly typed
functional languages, I'm totally psyched to start using Contracts on my