There is a straightforward way to "transliterate" Python into Haskell. This can be done by clever usage of monad transformers, which sounds scary, but it's really not. You see, due to purity, in Haskell when you want to use effects such as mutable state (e.g. the append and pop operations are performing mutation) or exceptions, you have to make it a little more explicit. Let's start at the top.

parse :: String -> SchemeExpr parse s = readFrom (tokenize s)

The Python docstring said "Read a Scheme expression from a string", so I just took the liberty of encoding this as the type signature ( String -> SchemeExpr ). That docstring becomes obsolete because the type conveys the same information. Now... what is a SchemeExpr ? According to your code, a scheme expression can be an int, float, symbol, or list of scheme expressions. Let's create a data type that represents these options.

data SchemeExpr = SInt Int | SFloat Float | SSymbol String | SList [SchemeExpr] deriving (Eq, Show)

In order to tell Haskell that the Int we are dealing with should be treated as a SchemeExpr , we need to tag it with SInt . Likewise with the other possibilities. Let's move on to tokenize .

tokenize :: String -> [Token]

Again, the docstring turns into a type signature: turn a String into a list of Token s. Well, what's a Token? If you look at the code, you'll notice that the left and right paren characters are apparently special tokens, which signal particular behaviors. Anything else is... unspecial. While we could create a data type to more clearly distinguish parens from other tokens, let's just use Strings, to stick a little closer to the original Python code.

type Token = String

Now let's try writing tokenize . First, let's write a quick little operator for making function chaining look a bit more like Python. In Haskell, you can define your own operators.

(|>) :: a -> (a -> b) -> b x |> f = f x tokenize s = s |> replace "(" " ( " |> replace ")" " ) " |> words

words is Haskell's version of split . However, Haskell has no pre-cooked version of replace that I know of. Here's one that should do the trick:

-- add imports to top of file import Data.List.Split (splitOn) import Data.List (intercalate) replace :: String -> String -> String -> String replace old new s = s |> splitOn old |> intercalate new

If you read the docs for splitOn and intercalate , this simple algorithm should make perfect sense. Haskellers would typically write this as replace old new = intercalate new . splitOn old , but I used |> here for easier Python audience understanding.

Note that replace takes three arguments, but above I only invoked it with two. In Haskell you can partially apply any function, which is pretty neat. |> works sort of like the unix pipe, if you couldn't tell, except with more type safety.

Still with me? Let's skip over to atom . That nested logic is a bit ugly, so let's try a slightly different approach to clean it up. We'll use the Either type for a much nicer presentation.

atom :: Token -> SchemeExpr atom s = Left s |> tryReadInto SInt |> tryReadInto SFloat |> orElse (SSymbol s)

Haskell doesn't have the automagical coersion functions int and float , so instead we will build tryReadInto . Here's how it works: we're going to thread Either values around. An Either value is either a Left or a Right . Conventionally, Left is used to signal error or failure, while Right signals success or completion. In Haskell, to simulate the Python-esque function call chaining, you just place the "self" argument as the last one.

tryReadInto :: Read a => (a -> b) -> Either String b -> Either String b tryReadInto f (Right x) = Right x tryReadInto f (Left s) = case readMay s of Just x -> Right (f x) Nothing -> Left s orElse :: a -> Either err a -> a orElse a (Left _) = a orElse _ (Right a) = a

tryReadInto relies on type inference in order to determine which type it is trying to parse the string into. If the parse fails, it simply reproduces the same string in the Left position. If it succeeds, then it performs whatever function is desired and places the result in the Right position. orElse allows us to eliminate the Either by supplying a value in case the former computations failed. Can you see how Either acts as a replacement for exceptions here? Since the ValueException s in the Python code are always caught inside the function itself, we know that atom will never raise an exception. Similarly, in the Haskell code, even though we used Either on the inside of the function, the interface that we expose is pure: Token -> SchemeExpr , no outwardly-visible side effects.

OK, let's move on to read_from . First, ask yourself the question: what side effects does this function have? It mutates its argument tokens via pop , and it has internal mutation on the list named L . It also raises the SyntaxError exception. At this point, most Haskellers will be throwing up their hands saying "oh noes! side effects! gross!" But the truth is that Haskellers use side effects all the time as well. We just call them "monads" in order to scare people away and avoid success at all costs. Mutation can be accomplished with the State monad, and exceptions with the Either monad (surprise!). We will want to use both at the same time, so we'll in fact use "monad transformers", which I'll explain in a bit. It's not that scary, once you learn to see past the cruft.

First, a few utilities. These are just some simple plumbing operations. raise will let us "raise exceptions" as in Python, and whileM will let us write a while loop as in Python. For the latter, we simply have to make it explicit in what order the effects should happen: first perform the effect to compute the condition, then if it's True , perform the effects of the body and loop again.

import Control.Monad.Trans.State import Control.Monad.Trans.Class (lift) raise = lift . Left whileM :: Monad m => m Bool -> m () -> m () whileM mb m = do b <- mb if b then m >> whileM mb m else return ()

We again want to expose a pure interface. However, there is a chance that there will be a SyntaxError , so we will indicate in the type signature that the result will be either a SchemeExpr or a SyntaxError . This is reminiscent of how in Java you can annotate which exceptions a method will raise. Note that the type signature of parse has to change as well, since it might raise the SyntaxError.

data SyntaxError = SyntaxError String deriving (Show) parse :: String -> Either SyntaxError SchemeExpr readFrom :: [Token] -> Either SyntaxError SchemeExpr readFrom = evalStateT readFrom'

We are going to perform a stateful computation on the token list that is passed in. Unlike the Python, however, we are not going to be rude to the caller and mutate the very list passed to us. Instead, we will establish our own state space and initialize it to the token list we are given. We will use do notation, which provides syntactic sugar to make it look like we're programming imperatively. The StateT monad transformer gives us the get , put , and modify state operations.

readFrom' :: StateT [Token] (Either SyntaxError) SchemeExpr readFrom' = do tokens <- get case tokens of [] -> raise (SyntaxError "unexpected EOF while reading") (token:tokens') -> do put tokens' -- here we overwrite the state with the "rest" of the tokens case token of "(" -> (SList . reverse) `fmap` execStateT readWithList [] ")" -> raise (SyntaxError "unexpected close paren") _ -> return (atom token)

I've broken out the readWithList portion into a separate chunk of code, because I want you to see the type signature. This portion of code introduces a new scope, so we simply layer another StateT on top of the monad stack that we had before. Now, the get , put , and modify operations refer to the thing called L in the Python code. If we want to perform these operations on the tokens , then we can simply preface the operation with lift in order to strip away one layer of the monad stack.

readWithList :: StateT [SchemeExpr] (StateT [Token] (Either SyntaxError)) () readWithList = do whileM ((\toks -> toks !! 0 /= ")") `fmap` lift get) $ do innerExpr <- lift readFrom' modify (innerExpr:) lift $ modify (drop 1) -- this seems to be missing from the Python

In Haskell, appending to the end of a list is inefficient, so I instead prepended, and then reversed the list afterwards. If you are interested in performance, then there are better list-like data structures you can use.

Here is the complete file: http://hpaste.org/77852

So if you're new to Haskell, then this probably looks terrifying. My advice is to just give it some time. The Monad abstraction is not nearly as scary as people make it out to be. You just have to learn that what most languages have baked in (mutation, exceptions, etc), Haskell instead provides via libraries. In Haskell, you must explicitly specify which effects you want, and controlling those effects is a little less convenient. In exchange, however, Haskell provides more safety so you don't accidentally mix up the wrong effects, and more power, because you are in complete control of how to combine and refactor effects.