Tableau External Services API: Haskell Expressions as Calculations
By: Tamas Foldi
Tags: Functional Programming, Dataviz, Tableau, Haskell

We all have our own Tableau Conference habits. Mine is to participate in the first-day hackathon, build something super cool that nobody understands and in the end win nothing. Tableau 2019 was no exception. Earlier this year, our team won the NYC Tableau Hackathon — and I had almost nothing to do with it. My tasks were limited to keep our squad hydrated, caffeinated and fed, so for TC19 I had no real pressure to go competitive. I was completely free and in a bit of an artsy mood so I decided to build one of my old dream projects:

adding the Haskell programming language as a calculation engine to Tableau, by using External Services API.

If you have no clue what does that means — or even if you do — read on.

Tableau External Services API?

External Services API is the most underrated, underdocumented API in the whole Tableau Ecosystem. It’s so neglected, that it doesn’t even belong to the Tableau’s developer platform. This is fine with me. In fact, it makes it all that much more interesting, I hate popular things, and frankly being a trailblazer these days is greatly appreciated.

I know TabPy, but what could be that “External API”?

If you search the term “ Tableau External Services”, you will find some documentation on Tableau’s help site about it: “Tableau supports a set of functions that you can use to pass expressions to external services for integration with R, MATLAB and Python.”, but none of these pages mentions “External API”. The only single document I found is here, buried three subpages down on TabPy’s Github wiki. While the page describes the API interface it forgets to mention what this REST API is actually doing.

So here is the deal: with the External Services API you can:

Add new supported programming languages to the Tableau Calculation engine (by script_ commands). Call web services directly as calculations without any TabPy, RServer or other middleware.

Pretty cool, huh? #2 works well when you start building your own, company-specific calculations or data processing functions in pure AWS Lambdas then invoke it directly from Tableau Desktop or Server. I honestly have no clue why Tableau keeps it a secret, but it’s definitely a good material for another hackathon ;-). However, as I’m about to add a new language to Tableau, let’s focus on #1.

Why Haskell?

“Being a special snowflake” is my number one mission during these hackathons. As a programming language, Haskell seems to have a similar mission, it is really different — in a good way — from other programming languages. To understand why Haskell is different, let me share a quote from the “ How to Learn Haskell “ guide:

First of all, you should probably pretend like you’ve never programmed before. If you run into a word like return or class don’t assume that you know what that means; chances are that you don’t. Here’s a general idea of where you stand, depending on what languages you already know well: C, C++, Java: Haskell will probably blow your mind. The notion of classes is almost completely different, return doesn’t mean what you think it does, the algorithmic patterns are distinct, you don’t get loops, and code doesn’t run in the order it’s typed on the screen. What!? Yeah. It’s pretty awesome.

Trust me, it is * really* awesome, especially when it goes to analytical expressions. Look at this Fibonacci calculation code snippet:

fix (scanl (+) 0 . (1:))

Every single character has its meaning. Fix function is typed recursive lambda generator, is an accumulator function, “.” is a function composition operator and “:” a list constructor. After staring the expression for a few seconds things starts to make sense.

Another example for calculating prime numbers:

primesT = sieve [2..]

where

sieve (p:xs) = p : sieve [x | x <- xs, rem x p > 0]

It looks exactly as the mathematical definition of prime numbers. This is one of Haskell’s values: its expressions are close to mathematical equations.

Let’s see how can we add these functions directly to Tableau.

The Code

The complete codebase for Haskell integration is not more than twenty lines. In different words, with less than a screen of code, you can add live data and custom calculations based on web services or new programming languages to Tableau by using External Services API.

data EvalRequest =

EvalRequest {

_script :: Text,

_data :: HashMap Text Value

} deriving (Show, Generic, ToJSON)



instance FromJSON EvalRequest

where parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = Prelude.drop 1 }



replaceArgsInScript (EvalRequest s d) = foldrWithKey replaceValuesScript s d

where getArrayText = T.toStrict . T.decodeUtf8 . encode

replaceValuesScript k v = replace k (getArrayText v)



command = replace_args . decode

where replace_args Nothing = "Json parse error"

replace_args (Just er) = unpack $ replaceArgsInScript er



executeCommand p = readProcess "mueval" ["-e", command p] []



infoResponse = "{\"description\": \"\", \"creation_time\": \"0\", \"state_path\": \"\", \"server_version\": \"0.8.7\", \"name\": \"haskell\", \"versions\": {\"v1\": {\"features\": {}}}}"



textAsJson t = setHeader "Content-Type" "application/json" >> text t



main = scotty 3000 $ do

get "/info" $

textAsJson infoResponse

post "/evaluate" $ do

jsonData <- body

cmdRes <- liftIO $ executeCommand jsonData

textAsJson $ T.pack cmdRes

In order to act like an external service you have to implement two REST API endpoints: /info and /evaluate . Info is fairly simple, it just returns with a static JSON file as defined and documented here.

The /evaluate is a bit more tricky, this is the actual endpoint called by the SCRIPT_STR , SCRIPT_INT and other SCRIPT functions. The definition of this endpoint is here, but basically it gets a block of code, set of named parameters with their provided values, then returns with the result. The expected POST request body from Tableau is a JSON dictionary with two elements:

A key data with a value that contains the parameter values passed to the code. These values are key-value pairs, following a specific convention for key names ( _arg1 , _arg2 , etc.).

with a value that contains the parameter values passed to the code. These values are key-value pairs, following a specific convention for key names ( , , etc.). A key script with a value that contains the Python code (one or more lines). Any references to the parameter names will be replaced by their values according to data .

The calculation SCRIPT_INT("sort _arg1", SUM([numbers])) will trigger the following /evaluate request:

POST /evaluate HTTP/1.1.



{"script":"sort _arg1",

"data" :{"_arg1":[1,2,3,4,5,6,8,7,9,10]}}

All we need to do is to replace _arg1 with actual values, execute script and return. From the above request, the code we should execute is:

sort [1,2,3,4,5,6,8,7,9,10]

Nice and easy.

Getting up and ready our External Server

You can download the project from https://github.com/tfoldi/tc19-haskell-ext/. All you have to do is install stack in case you don’t have it and call stack run .

Next time you start Tableau Desktop you should set the external service provider in Help, Settings and Performance, Manage External Service Connection.

Set connection to localhost, port 3000

You should be good to go, let’s give a try and define a simple sort calculation:

And if all works well, we should see these results:

Fibonacci and Prime numbers in Tableau with Haskell

Pretty impressive, we just added Haskell support to Tableau with twenty lines code!

Haskell in Tableau

There are more advanced use cases where you can use the combined power of Extensions and Haskell to do interactive data exploration, but that’s a different blog post.

As always, if you have any questions or comments, just drop a line!

And by all means, leave a comment and let me know what you think.