Fold a String into an Int type

While working my way through (Sullivan, Goerzen & Stewart, 2009), I came across the problem of converting a String to an Int using folds. This isn't that difficult a problem, but because I'm still a beginner to Haskell, I had to think about it for a few days.

Finally, I came up with the following (unsafe) solution using foldl.
import Data.Char (digitToInt)

asInt :: String -> Int
asInt [] = 0
asInt (x:xs) | x == '-'  = (-1) * asInt xs
             | otherwise = sum $ foldl helper [] $ zip (reverse [0..(length (x:xs) - 1)]) (x:xs)
    helper :: [Int] -> (Int,Char) -> [Int]
    helper totalList (order,digit) = digitToInt digit * 10 ^ order : totalList
I separated the state of summing from the rest of the problem, using the fold to  'loop' over the list and raise each digit to the appropriate magnitude based on its position, just as one would in an imperative language. Thus, I believe this is a hybrid solution that uses both recursion and a fold (because of the way I've written my helper function).

foldl is of course not the most memory-efficient implementation, again according to the same chapter in (Sullivan et al., 2009). But because the maximum number of digits we may have in this type is 9 (it ranges from  \([-2^{29},2^{29}-1]\)), I don't think lazy evaluation becomes an issue unless you're performing this operation many times, or in large numbers at the same time.

This being said, I did find a solution online by Ben Doerr that appears to be quite a little bit simpler. His solution also does not make use of any recursion, likely due to his initial element being 0 instead of the empty list as I've used.

Of course, one of the simplest approaches I've come across is read someString :: Int.

And because no post is complete without a little Python script, here's an implementation using the same concept I used above in Haskell with
#! /usr/bin/env python3.6
# -*- coding: utf-8 -*-
# vim:fenc=utf-8

from typing import Text, Callable, List, Tuple
from fn.op import foldl

Char = Text

def asInt(string: str) -> int:
    """ Convert a str to an int. """
    if string[0] == '-':
        return -asInt(string[1:])

    def digitToInt(c: Char) -> int:
        assert len(c) == 1
        return int(c)

    def helper(totalList: List[int], tup: Tuple[int, Char]) -> List[int]:
        order, digit = tup
        value = digitToInt(digit) * 10 ** order
        return totalList

    indexes = reversed(range(len(string)))

    return sum(foldl(helper, [])(zip(indexes, string)))

if __name__ == '__main__':
    integer = asInt("54321")
    print(integer)            # 54321
    print(type(integer))      # 
It's easiest to understand how this works if you add a print(totalList) command before return in helper, which prints the following.
[50000, 4000]
[50000, 4000, 300]
[50000, 4000, 300, 20]
[50000, 4000, 300, 20, 1]
which is then of course summed and returned.


O'Sullivan, B., Goerzen, J., & Stewart, D. (2009). Real World Haskell. Sebastopol, CA: O'Reilly.