Skip to content

Instantly share code, notes, and snippets.

@danking
Created January 11, 2016 23:20
Show Gist options
  • Save danking/0400007107f862991182 to your computer and use it in GitHub Desktop.
Save danking/0400007107f862991182 to your computer and use it in GitHub Desktop.
data Criteria = Criteria (Conjunction (Disjunction Term)) (Disjunction (Conjunction Term))
deriving Show
data Term = Term String
deriving Show
data Conjunction a = Conjunction [a]
deriving Show
data Disjunction a = Disjunction [a]
deriving Show
class ToSql a where
toSql :: a -> String
toComposableSql :: ToSql a => a -> String
toComposableSql x = "(" ++ (toSql x) ++ ")"
instance ToSql Criteria where
toSql (Criteria inclusion exclusion) =
(toComposableSql inclusion) ++ " MINUS " ++ (toComposableSql exclusion)
instance ToSql a => ToSql (Conjunction a) where
toSql (Conjunction conjuncts) =
foldl (\x y -> x ++ " INTERSECT " ++ y)
"(select * from observation_fact)"
(map toComposableSql conjuncts)
instance ToSql a => ToSql (Disjunction a) where
toSql (Disjunction disjuncts) =
foldl (\x y -> x ++ " UNION " ++ y)
"(select * from observation_fact where 1=0)"
(map toComposableSql disjuncts)
instance ToSql Term where
toSql (Term id) =
"select * form observation_fact where concept_cd = '"++id++"'"
@danking
Copy link
Author

danking commented Jan 11, 2016

What the Heck is class?

The block:

class ToSql a where
  toSql :: a -> String

is equivalent to the Java code:

interface ToSql {
  public String toSql();
}

What the Heck is instance?

The instance definitions that follow are all basically implementations of this interface. For example:

instance ToSql Criteria where
  toSql (Criteria inclusion exclusion) =
    (toComposableSql inclusion) ++ " MINUS " ++ (toComposableSql exclusion)

Is something like:

class Criteria implements ToSql {
  public String toSql() {
    return toComposableSql(inclusion) + " MINUS " + toComposableSql(exclusion);
  }
}

Assuming we have an appropriate definition of toComposableSql somewhere else in that class.

What are fat arrows?

Fat arrows play the same role as type constraints in Java like:

class Foo<A extends Bar> { ... }

So we can read this:

instance ToSql a => ToSql (Conjunction a) where
  toSql (Conjunction conjuncts) =
    foldl (\x y -> x ++ " INTERSECT " ++ y)
          "(select * from observation_fact)"
          (map toComposableSql conjuncts)

as:

public class Conjunction<A extends ToSql> implements ToSql {
  private final List<A> conjuncts;
  // constructors, etc.
  public String toSql() {
    return conjuncts.stream()
      .map(toComposableSql)
      .reduce("(select * from observation_fact)",
              (x, y) -> x + " INTERSECT " + y))
  }
}

Conclusion

I think this code is very declarative. It's easy to read and believe that it's correct. There's also never any NullPointerExceptions.

We can write Java code like this too, as my examples show. However, Java can often feel a lot more verbose than Haskell. Moreover, in Haskell you can monkey patch existing classes (like String) with new interfaces, but we cannot do this in Java.

Some Notes on The Type Parameter in ToSql

In Haskell, the ToSql a type takes a type parameter, the a. In Java, we don't need the type parameter because our function has the implied this argument. If we wanted to write a binary function in Haskell like:

class AddAble a where
  add :: a -> a -> String

then the equivalent Java code is:

interface AddAble<A> {
  public String add(A a);
}

Which is different from add(AddAble a) because the former requires that the argument is the same class, not just implements the same interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment