Epoch fail, Clojure edition

A while back, I wrote about doing epoch calculations in Elixir. Recently, I tried the same sort of thing in Clojure. Since Clojure sits on top of Java, I was not optimistic. Historically, Java has not had a good story to tell when it comes to date-time manipulation. But Java 1.8 added a new java.time package, so maybe it’s better now?

Clojure has clj-time, which wraps joda-time. Joda Time is the third-party library that most Java folks used before version 1.8 (and still do, I guess, if they’re stuck on 1.7 or 1.6 for some reason). Perhaps clj-time will move to java.time eventually, but for now at least they are still on joda-time. Maybe it would have been more sensible for me to just have used clj-time, but I wanted to use the java.time package.

I discovered there is a Clojure java-time (with a dash) that wraps java.time (with a period) and I started out doing that, but eventually abandoned it. When I got to the google-calendar calculation, I needed the .toInstant method, and java-time didn’t seem to supply it.

So, I changed to using java.time directly (if you look back in the git history of Epochs-clojure, you can see where I switched). Since Clojure’s Java interop is so seamless, it turned out pretty nice! Here is that google-calendar portion:

(defn google-calendar
  "Google Calendar time seems to count 32-day months from the day
  before the Unix epoch. @noppers worked out how to do this."
  [n]
  (let [seconds-per-day (* 24 60 60)
        utc (java.time.ZoneOffset/UTC)
        n-days (quot n seconds-per-day)
        n-seconds (rem n seconds-per-day)
        g-months (quot n-days 32)
        g-days (rem n-days 32)]
    (-> (java.time.LocalDateTime/ofEpochSecond (- seconds-per-day) 0 utc)
        (.plusDays g-days)
        (.plusMonths g-months)
        (.toInstant utc)
        (.plusSeconds n-seconds))))

Java time has Instants, which are the naive date-times that I want to do everything with. Similar to Elixir, java.time won’t let me add days or months to an Instant. So I create a LocalDateTime, add the days and months, then convert to an Instant and add the seconds.

Not bad. Not as nice as the Perl version, but better than the Elixir version. At least java.time gives me the .plusMonths method I need, even if it does make me choose a timezone to get it. And, remarkably, it’s just Java. I usually can’t bear to look at Java code, but Clojure makes it almost pretty.

I look forward to clj-time or java-time supplying more Clojure-like access to java.time in the future. But in the meantime, accessing it directly isn’t that bad!


(Update: 2017-08-06) I went to see if I could add .toInstant support for LocalDateTimes in java-time. I figured rather than just complain, I’d send a pull request. In so doing, I discovered it is already there! If I have a LocalDateTime in ldt, then this Java interop code

(.toInstant ldt java.time.ZoneOffset/UTC)

can be written as this java-time code

(instant ldt (zone-offset 0))

I think I’m going to leave Epochs-clojure as Java interop for now, but maybe I’ll switch back to java-time in the future if things start to get too Java-y.

Advertisements
Epoch fail, Clojure edition

Rusty Perl

I wanted to call Rust from Perl, so I tried to follow along with this blog post which does exactly that. But it was written before the release of Rust 1.0, so not everything still works. Here’s what I did:

Create a new Rust project called points.

$ cargo new points
     Created library `points` project
$ cd points

Add a lib section to the Cargo.toml file to create a .so instead of a .rlib.

[package]
name = "points"
version = "0.1.0"
authors = ["oylenshpeegul <oylenshpeegul@gmail.com>"]

[lib]
name = "points"
crate-type = ["dylib"]

Now edit the src/lib.rs as @pauldwoolcock describes, but there’s no deriving, no box, no int, and abs_sub is deprecated.

#[derive(Copy, Clone)]
pub struct Point { x: i64, y: i64 }

struct Line { p1: Point, p2: Point }

impl Line {
    pub fn length(&self) -> f64 {
        let xdiff = self.p1.x - self.p2.x;
        let ydiff = self.p1.y - self.p2.y;
        ((xdiff.pow(2) + ydiff.pow(2)) as f64).sqrt() 
    }
}

#[no_mangle]
pub extern "C" fn make_point(x: i64, y: i64) -> Box<Point> {
    Box::new( Point { x: x, y: y } )
}

#[no_mangle]
pub extern "C" fn get_distance(p1: &Point, p2: &Point) -> f64 {
    Line { p1: *p1, p2: *p2 }.length()
}

#[cfg(test)]
mod tests {
    use super::{Point, get_distance};

    #[test]
    fn test_get_distance() {
        let p1 = Point { x: 2, y: 2 };
        let p2 = Point { x: 4, y: 4 };
        assert!((get_distance(&p1, &p2) - 2.828427).abs() < 0.01f64);
    }
}

Now try running the tests!

$ cargo test
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running target/debug/deps/points-0a1a2813ecad97ba

running 1 test
test tests::test_get_distance ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

If we do a cargo build, we’ll get a debug build and our libpoints.so will be in one location

$ cargo build
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs

If we do a cargo build --release, we’ll get a release build and our libpoints.so will be in another location.

$ cargo build --release
    Finished release [optimized] target(s) in 0.0 secs

To use libpoints.so from Perl, we’ll create a perl directory with our points.pl script in it.

$ mkdir perl
$ touch perl/points.pl

We’ll use the FindBin module to link to the libpoints.so file relative to where we are now. And we’ll use a debug flag to link to either the debug or the release.

#!/usr/bin/env perl

use v5.24;
use warnings;
use FindBin;
use FFI::Raw;

my $debug = shift;

my $libpoints = "$FindBin::Bin/../target/release/libpoints.so";
if ($debug) {
    $libpoints = "$FindBin::Bin/../target/debug/deps/libpoints.so";
}

my $make_point = FFI::Raw->new(
    $libpoints,
    'make_point',
    FFI::Raw::ptr,
    FFI::Raw::int, FFI::Raw::int,
);

my $get_distance = FFI::Raw->new(
    $libpoints,
    'get_distance',
    FFI::Raw::double,
    FFI::Raw::ptr, FFI::Raw::ptr,
);

my $p1 = $make_point->call(2,2);
my $p2 = $make_point->call(4,4);

my $result = $get_distance->call($p1, $p2);
say "The distance from (2,2) to (4,4) is $result (the square root of 8).";

Now we should be able to run the Perl script from anywhere, with either the debug build or the release build.

$ perl/points.pl 1
The distance from (2,2) to (4,4) is 2.82842712474619 (the square root of 8).

$ perl/points.pl 
The distance from (2,2) to (4,4) is 2.82842712474619 (the square root of 8).

All of these files are on github.

Rusty Perl

Metacpan Download URL

This morning I read that we can now get the download URL for a CPAN module from the Metacpan API! For example, if we visit this URL

$ curl https://api-v1.metacpan.org/download_url/Path::Tiny
{
   "download_url" : "https://cpan.metacpan.org/authors/id/D/DA/DAGOLDEN/Path-Tiny-0.096.tar.gz",
   "version" : "0.096",
   "status" : "latest",
   "date" : "2016-07-03T01:36:29"
}

we get this blob of JSON. If we just want the URL, we could run it through jq

$ curl -s https://api-v1.metacpan.org/download_url/Path::Tiny | jq .download_url
"https://cpan.metacpan.org/authors/id/D/DA/DAGOLDEN/Path-Tiny-0.096.tar.gz"

OALDERS does the same thing in Perl, but it uses three different CPAN modules. HTTP::Tiny is in the standard library, right? Oh, but it needs help from IO::Socket::SSL and Net::SSLeay to get an https URL. And we still need something to encode the URI and something to decode the JSON. Here’s my first crack at it.

#!/usr/bin/env perl

use v5.24;
use warnings;
use HTTP::Tiny;
use JSON;
use URI::Encode qw(uri_encode);

my $module = shift // die "Usage: $0 module\n";

my $uri = uri_encode("https://api-v1.metacpan.org/download_url/$module");

my $res = HTTP::Tiny->new->get($uri);
die "Failed!\n" unless $res->{success};

say decode_json($res->{content})->{download_url};

We didn’t need to use LWP, but we still needed help from CPAN. If we can’t do it with the standard library, why not use Mojolicous? This is a web framework, of course, but it includes some excellent client-side tools too. Here is the same thing using the Mojolicious user agent and JSON decoder.

#!/usr/bin/env perl

use v5.24;
use warnings;
use Mojo::UserAgent;

my $module = shift // die "Usage: $0 module\n";

say Mojo::UserAgent->new
    ->get("https://api-v1.metacpan.org/download_url/$module")
    ->res
    ->json
    ->{download_url};

We can even make it a one-liner using the delightful ojo module!

$ perl -Mojo -E 'say g("https://api-v1.metacpan.org/download_url/".shift)->json->{download_url}' Path::Tiny
https://cpan.metacpan.org/authors/id/D/DA/DAGOLDEN/Path-Tiny-0.096.tar.gz

I’m a little disappointed that it’s not easy to do something as simple as this with just the standard library, but if we use CPAN then we have lots of choices. TMTOWTDI!

Metacpan Download URL

Chalk one up for sed!

Hey, I just saw on @climagic that sed can replace the second occurrence of a regex match with a simple modifier!

$ sed s/i/u/2 <<< fizzbizz
fizzbuzz

Perl’s substitute operator comes from sed and looks pretty much the same, but it can’t do this.

$ perl -pe 's/i/u/2' <<< fizzbizz
Unknown regexp modifier "/2" at -e line 1, at end of line
Execution of -e aborted due to compilation errors.

We’d have to do some awkward counting maneuver with an eval or something.

$ perl -pe 's/i/++$n - 2 ? "i" : "u"/ge' <<< fizzbizz
fizzbuzz

I used to use awk and sed quite a bit, but since learning Perl I find I don’t have much use for them anymore. I miss sed. But here’s one more reason not to let my sed skills atrophy completely.

Chalk one up for sed!

Perl 5.24 on a stick

I just upgraded my Perl thumb drive to Strawberry Perl 5.24.0.1!

pic of Perl thumb drive

I simply reformatted it and unzipped the new one onto it

cd /media/tim/Strawberry
unzip ~/Downloads/strawberry-perl-5.24.0.1-64bit-PDL.zip

When I put the thumb drive in a Windows machine, it was mounted as the I: drive, so now I can use Strawberry Perl from cmd.exe

Microsoft Windows [Version 10.0.10586]
(c) 2015 Microsoft Corporation. All rights reserved.

C:\Users\tim>I:\perl\bin\perl -E "say qq{Hello, Perl $]}"
Hello, Perl 5.024000

or from PowerShell

PS C:\Users\tim> I:/perl/bin/perl -E "say qq{Hello, Perl $]}"
Hello, Perl 5.024000

Supergood!

Perl 5.24 on a stick

GADSWAD

Recently, on the Python-Dev mailing list, Guido van Rossum said the transition to Python 4 won’t be like the transition to Python 3 (“we’ve learned our lesson”).

Yukihiro Matsumoto has said similar things (without the cheap shot at Perl, naturally) recently at RubyConf 2015 and at Full Stack Fest 2015 when he talked about Ruby 3.0. In both of those talks, he gave a pretty thorough outline of the similar transitions that Perl, Python, and Ruby have gone through over the last 10 or 15 years. He talks about how Perl started over from scratch, Python made a set of backwards-compatiblity breaking changes all at once, and Ruby made backwards-compatiblity breaking changes gradually over several minor versions.

Python 3 was released nearly seven years ago, yet a majority of Python users are still on Python 2. It looks as though some might never switch. Ruby users, on the other hand, gradually transitioned from 1.9.1 to 1.9.2 to 1.9.3. They find themselves at Ruby 2 without really noticing.

Both GvR and Matz are looking at it like this

Perl 5   -> Perl 6
Python 2 -> Python 3
Ruby 1.8 -> Ruby 2

and feeling pretty good about themselves. Perl 6 isn’t even out yet, so they both did okay, right? And now they’re looking ahead at things like concurrency and performance that will be necessary for the future.

Perl 5   -> Perl 6
Python 2 -> Python 3  -> Python 4
Ruby 1.8 -> Ruby 2    -> Ruby 3

But despite its decline in popularity, I think Perl 5 has maintained feature parity with Python and Ruby. Since Perl 5.10 opened the floodgates, the updates to Perl are collectively known as Modern Perl. I use Modern Perl every day at my job and it’s fine. While Python 3 and Ruby 2 have kept up with it, more or less, they are not a quantum leap ahead by any stretch of the imagination. So I think the situation today looks more like this:

Perl 5   -> Modern Perl
Python 2 -> Python 3
Ruby 1.8 -> Ruby 2

Perl 6 is threatening to finally come out later this month and it is already prepared for the future. That is, I believe we should be comparing Perl 6 to Python 4 and Ruby 3.

Perl 5   -> Modern Perl -> Perl 6
Python 2 -> Python 3    -> Python 4
Ruby 1.8 -> Ruby 2      -> Ruby 3

Of course, there’s still the whole issue of adoption. If Perl 6 is adopted as slowly as Python 3 (or even more slowly!), then it may never matter how futuristic it is.

I think concurrency is going to matter a great deal in the near future, so my recreational programming time lately has been dedicated to things like Clojure, Elixir, Go, and Rust. I’m betting my professional programming time will go that way soon too. Sequential programming is doomed. We’re going to have to figure out concurrency or find another profession.

GADSWAD