As a newbie to Haskell, thinking in a way that's idiomatic is a challenge. I have a list of dyads. I want to weight dyad members according to two maps, one indicating the direction of the weighting and the other providing the weighting itself. In the following code 1 indicates that the bottom member of the dyad receives the weight; -1 the top member of the dyad receives the weight; 0 that both members receive the weight. In all cases it is the difference between dyad members that determines the weight and direction
My question is how can I re-use the definitions for top and bottom weight allocation in the case where the weighting is equal? Every source I have consulted so far seems to indicate that guards can have only one outcome - which I suspect is the proper Haskell way...
allocateWeight :: [[Integer]] -> (Integer, Integer, Maybe Double)
allocateWeight [x, y]
|direction <= Just 1 = assignBottom
|direction <= Just (-1) = assignTop
|direction <= Just 0 = (?? assignBottom and assignTop ??)
where diff = (abs(x!!1 - y!!1))
direction = Map.lookup diff getWeightDirection
weight = Map.lookup diff schemaCAT
assignBottom = (head x, last x, weight)
assignTop = (head y, last y, weight)
Okay Ive been asked to clarify further. Ill cut the non essentials as they will only cloud the issue.
Stage 1: start with a list of values eg: [6, 3, 8, 11, 2] : values constrained between 1 and 12.
Stage 2: permutate them into dyads: [(6,3),(6,8), (6,11), (6,2), (3, 8), (3, 11),(3, 2),(8, 11)(8, 2),(11,2)]
Stage 3: Get the absolute difference of each pair:[(3),(2),(5),(4),(5), (8), (1), (3), (6),(9)]
Stage 4: According to the difference between them ONE member of each dyad (with 6 being the exception) will receive a weighting; this is pre-determined in the following map:
getWeightDiretion :: Map.Map Integer Integer -- determine weight direction
getWeightDirection = Map.fromList $
[(1, -1),
(2, -1),
(3, 1),
(4, 1),
(5, -1),
(6, 0),
(7, 1),
(8, -1),
(9, -1),
(10, 1),
(11, 1),
(12, 1))]
As stated if the value of map lookup is 1 weight goes to bottom; -1 to top. The problem is when looking up key is 6 when neither of the dyad members are weighted more than the other: that is, they receive weight equally. The weightings are also predetermined by looking keys up in this map:
schemaCAT :: Map.Map Integer Double --Cross-At-Tail weighting scheme
schemaCAT = Map.fromList $
[(12, 0.985),
(11, -0.7),
(10, 0.2),
(9, 0.4),
(8, 0.6),
(7, 0.9),
(6, 0.08),
(5, 0.8),
(4, 0.7),
(3, 0.5),
(2, 0.1),
(1, -0.8),
(999, 0.25)]
The input to the allocateWeights function is in the form [[(-1, 6), (0, 3)], [.... where the first member of each tuple in each sub-list is a transposing factor - which is not relevant here; the second is one of a permutation pair. Each sub-list in the input represents one permutation. The allocateWeights function operates on x!!1 of each tuple in each sub-list directly.
After applying the above I should end up with a list a tuples [(-1, 3, 0.5)....] The second tuple member is the member of the tuple that receives the weight and the third is the weight itself (I'll leave out what the first member of the tuple is as is not important. The same with key 999 which is a special case).
As far as I understand it I have two problems. First, maps return Maybes and using these values in guards is problematic, Second, is the issue of using two definitions on the right hand side of an expression in a guard. Still struggling with 'Justs' and 'Maybes' :(
THX....
I’m not sure how you intend to combine the results when you write “assignBottom and assignTop”, but I can offer a few suggestions that may help.
When you find yourself in a situation like this, with a tangle of primitive types that are frustrating to work with, it’s usually a sign that you’re trying to do too many things at once in a single function, and that you need to introduce auxiliary functions or data types and compose them to solve the problem. When I look at this code:
allocateWeight :: [[Integer]] -> (Integer, Integer, Maybe Double)
allocateWeight' [x, y]
|direction <= Just 1 = assignBottom
|direction <= Just (-1) = assignTop
|direction <= Just 0 = (?? assignBottom and assignTop ??)
where diff = (abs(x!!1 - y!!1))
direction = Map.lookup diff getWeightDirection
weight = Map.lookup diff schemaCAT
assignBottom = (head x, last x, weight)
assignTop = (head y, last y, weight)
What jumps out at me are the following:
Is it allocateWeight or allocateWeight'?
The input is [[Integer]], but you say that the inner lists are known to be “dyads”, so this list probably wants to be a list of pairs [(Integer, Integer)].
Furthermore, allocateWeight assumes that there are only two elements in the outer list with the pattern [x, y]. Is this function meant to operate on the whole list, or each pair in the list?
The result type (Integer, Integer, Maybe Double), doesn’t explain its meaning; it would benefit from being a data type.
A series of guards direction <= Just … suggests that you want to first pattern-match on the Maybe value and then compare its range.
Your guard values are overlapped. Guards are checked in order, Nothing compares less than Just x for any x, and Just x <= Just y if x <= y. So if the value isn’t found, Nothing will compare less than Just 1 and the first guard will be taken; and if the value compares less than or equal to Just (-1) or Just 0, then it must already have compared less than or equal to Just 1, so the other two guards will never fire.
diff = (abs(x!!1 - y!!1)) again suggests that these don’t want to be lists; the indexing operator !! is best avoided in ordinary code. (It does have some use in cases where, for example, you’re using a list for memoisation, but that’s for another time.)
The weight directions are always -1, 0, or 1, which is asking to be a data type.
The returned weight probably shouldn’t be wrapped in a Maybe.
So just in terms of code organisation, here’s how I would approach this:
-- A pair of a value with a weight.
data Weighted a = Weighted a Double
deriving (Show)
-- The direction in which to weight a pair.
data WeightDirection
= WeightBottom
| WeightTop
| WeightBoth
-- Use data type instead of integer+comment.
getWeightDirection :: Map.Map Integer WeightDirection
getWeightDirection = Map.fromList
[ (1, WeightTop)
, (2, WeightTop)
, (3, WeightBottom)
, (4, WeightBottom)
, (5, WeightTop)
, (6, WeightBoth)
-- …
]
-- Allocate weights for a single pair.
allocateWeight :: (Integer, Integer) -> [Weighted Integer]
allocateWeight (x, y) = case Map.lookup diff getWeightDirection of
Just direction -> case Map.lookup diff schemaCAT of
Just weight -> case direction of
WeightBottom -> [Weighted x weight]
WeightTop -> [Weighted y weight]
WeightBoth -> [Weighted x weight, Weighted y weight]
-- What do you want to do in this case?
Nothing -> error ("direction for " ++ show diff ++ " not found")
-- What do you want to do if the difference isn’t found?
-- Raise an error like this, or return a default?
Nothing -> error ("weight for " ++ show diff ++ " not found")
where
diff = abs (x - y)
I’ve guessed that when you want to allocate weight “equally” to both values, that you want to include both values in the result, each with their own weight, so I’ve changed allocateWeight to return a list. If you want to combine the weights in a different way, say, (x * weight / 2 + y * weight / 2) or something like that, then you may not need that. Using lists, as to your particular question, you could also write: bottom = [Weighted x weight] and top = [Weighted y weight], and then return bottom, top, or their concatenation bottom ++ top as needed.
But this illustrates what I want to show: with this function for operating on a single pair, you can compute the results for a whole list of pairs by mapping it over the input list and concatenating the results, using those functions separately or using concatMap:
allocateWeight :: (Integer, Integer) -> [Weighted Integer]
map allocateWeight :: [(Integer, Integer)] -> [[Weighted Integer]]
concatMap allocateWeight :: [(Integer, Integer)] -> [Weighted Integer]
You may be asking: what happened to the “extra” values you were keeping alongside each pair? The answer is that this function shouldn’t be concerned with them; generally the approaches are to extract the portions you need (with map or similar) and recombine the results (e.g. using zipWith prior to concat), or parameterise this function so it can only see the portions of a value that it needs, e.g.:
allocateWeightBy :: (a -> Integer) -> (a, a) -> [Weighted a]
allocateWeightBy getValue (x, y) = …
-- Since ‘x’ has a polymorphic type, it enforces that
-- the only thing you can do with it is call ‘getValue’
-- or return it in the list of weighted values.
The other thing is that if you’re struggling with Maybe results from looking up keys that may not be present in a Map, you may just need practice, but you could also benefit from trying different approaches, like:
Use case wherever you have a lookup to explicitly handle the absence of a value. It’s verbose but it works and can be made more idiomatic later.
If the Nothing case should raise an error, use the unsafe indexing operator (Data.Map.!). (Like !! this can be a sign of poor data structuring, and can bite you later.)
If you just want to provide a default value, use functions like fromMaybe found in Data.Maybe, e.g. fromMaybe (defaultWeight :: Double) (maybeWeight :: Maybe Double) :: Double.
Use a function instead of a Map, with a fallthrough case for a key that’s not found.
Related
I have a list with the type `[(Int, Char, Int)]'. E.g:
[(1, 'x', 1), (1, 'y', 2), (2, 'x', 1)]
The first Int is the number of times the Char appears and the second Int is to differentiate the same char from each other. For example, there could be x1 and x2.
I want to join elements of that list that have the same 2nd and 3rd element. In the case of the list above, it would become [(3, 'x', 1), (1, 'y', 2)] (the first and third tuples from the initial list were added together).
I've looked into zipWith and list comprehensions, but none of them seem to work. Is there any other way that I'm not thinking about that might work here?
The two functions you want to use are Data.List.sortBy and Data.List.groupBy.
If we sort by comparing the second and third elements of each tuple, we get the entries in the list sorted by variable and exponent. This is accomplished by passing a lambda which uses pattern macthing to extract and compare just those elements.
import Data.List
lst = [(1, 'x', 1), (1, 'y', 2), (2, 'x', 1)]
lst' = sortBy (\(_, a, b) (_, a', b') -> (a,b) `compare` (a',b')) lst
-- [(1,'x',1), (2,'x',1), (1,'y',2)]
Now we need to group based on the second and third values. groupBy will not work the way you need on an unsorted list, so don't skip that step.
The lambda being passed to groupBy here should look very familiar from the previous example.
lst'' = groupBy (\(_, a, b) (_, a', b') -> a == a' && b == b') lst'
-- [[(1,'x',1), (2,'x',1)], [(1,'y',2)]]
Now summing the first elements of the tuples and incorporating the other information is trivial with list comprehensions.
We get the variable and exponent info from the first element in the list and bind those to x and y respectively, then sum up the first coefficients and build a new tuple.
[let (_,x,y) = lst!!0 in (sum [c | (c,_,_) <- lst], x, y) | lst <- lst'', not (null lst)]
-- [(3,'x',1), (1,'y',2)]
First of all, I would suggest working with more meaningful domain types. A 3-tuple of built-in types could mean a lot of different things. By defining a new type and naming the components, you make everything clearer and prevent mistakes like getting the two Ints mixed up:
type Power = Int
type Coefficient = Int
data Exp var = Exp var Power deriving (Show, Eq, Ord)
data Term var = Term Coefficient (Exp var) deriving Show
What you're doing looks a lot to me like combining terms in polynomials, so I've defined types that make sense in that context. You may prefer different names, or a different structure, if you're actually doing something else.
Now you're looking for a function of type [Term Char] -> [Term Char], which groups together like Exps. Generally Data.Map.fromListWith is a great tool for grouping list items together by a key:
import qualified Data.Map as M
combine :: Ord a => [Term a] -> M.Map (Exp a) Coefficient
combine = M.fromListWith (+) . map toTuple
where toTuple (Term coef exp) = (exp, coef)
Then all that's left is to re-inflate the Map we've extracted to a list again:
simplify :: Ord a => [Term a] -> [Term a]
simplify = map fromTuple . M.assocs . combine
where fromTuple (exp, coef) = Term coef exp
And indeed, we get the grouping you hoped for:
*Main> simplify [Term 1 (Exp 'x' 1), Term 1 (Exp 'y' 2), Term 2 (Exp 'x' 1)]
[Term 3 (Exp 'x' 1),Term 1 (Exp 'y' 2)]
In Haskell document, sometime I find the word bottom (_|_). Is it correct that a bottom (_|_) is a value cannot be further broken down?
Example,
Non-bottom:
a Num is not, because, it an instance of class Num.
1 is not, because, it is a Num.
a String is not, because, it is a list of Char
"aa" is not, because it is a String
...
A-Bottom: following are all a bottom, because they cannot be further broken down.
a Char
an Int
1::Int
a Word
1::Word
Apple and Orange in data Fruit = Apple | Orange
...
Your intuition is incorrect. A bottom is a fictional value representing a non-terminating program. In Haskell, because of laziness, this value is present in any type.
For example:
x1 :: Integer
x1 = 42 -- not a bottom
x2 :: Integer
x2 = sum [1..] -- bottom, it does not terminate
x3 :: (Integer, String)
x3 = x3 -- bottom, infinite recursion
x4 :: (Integer, String)
x4 = (x2, "hello") -- not a bottom, but a pair (_|_, "hello")
We also pretend that undefined and error ".." are bottoms, since these are exceptional values of expression that fail to evaluate to a "proper" value in their type.
The name bottom comes from theoretical computer science, where mathematical models of computation are studied. In practice, it is common there to "order" the values in functional programming languages so that "non-termination" corresponds to the smallest possible value, which lies at the "bottom" of the ordering, hence the "bottom" name.
E.g. the following pairs are in increasing order.
p1 :: (Integer, Integer)
p1 = p1 -- bottom, infinite recursion
p2 :: (Integer, Integer)
p2 = (sum [1..], sum [1..]) -- not a bottom, but (_|_, _|_) which is only slightly larger
p3 :: (Integer, Integer)
p3 = (42, sum [1..])
-- (42, _|_) which is larger since it's more defined
-- (fewer bottoms inside, roughly speaking)
p4 :: (Integer, Integer)
p4 = (42, 23)
-- even larger, no larger values exist from here since it's a completely defined value
You can also make infinitely increasing sequences in some types:
l1 :: [Int]
l1 = l1 -- bottom
l2 :: [Int]
l2 = 1 : l1 -- not a bottom, 1 : _|_
l3 :: [Int]
l3 = 1 : 2 : l1 -- more defined, 1 : 2 : _|_
l4 :: [Int]
l4 = 1 : 2 : 432 : l1 -- more defined
-- etc. we can go on forever
This ordering of values is important in programming language theory because recursive definitions always find the least value satisfying the recursive equation.
E.g. the equation x1=x1 in Int is satisfied by _|_, 0, 1, -1, ..., but if we consider that it's a recursive definition, we have to take the least solution, namely _|_.
Similarly, in p :: (Integer, Integer) defined as p = (32, snd p), we find the solutions (32, _|_), (32, 0), (32, 1), (32, -1), .... but we need to take the least so we have (32, _|_). This models the fact that when we evaluate the expression the second component is never defined (because of infinite recursion).
The following definitions represent a shape composed of coloured squares at specific coordinates on a grid:
type AltShape = [Point]
data Point = P Colour (Int,Int) deriving Eq
data Colour = Black | Red | Green | Blue deriving Eq
I should asume that coordinates always are positive and the coordinate (0,0) refers to the top left square of the picture, and the y-coordinates grow downwards
A Red L-shape could be represented by
lShape = [P Red (0,0), P Red (0,1), P Red (0,2), P Red (1,2)]
A different way to represent such shapes is as a list-of-lists, one list for each row:
type Shape = [Row]
type Row = [Square]
type Square = Maybe Colour
For example, the red L-shape above would be represented by the following value of type Shape:
lShape2 = [[x,o]
,[x,o]
,[x,x]] where x = Just Red
o = Nothing
My task is to define a function toShape :: AltShape -> Shape that converts from a AltShape to an Shape. I had another task to define a function fromShape :: Shape -> AltShape but where data Shape = S [Row]. I found that rather simple and wrote it like this:
fromShape :: Shape -> AltShape
fromShape (S rows) = [ P c (x,y) | (y,row) <- index rows, (x,Just c) <- index row]
where index = zip [0..]
However, I am having more trouble with this one. I started by creating the function
colourAndCoords :: Point -> (Colour,(Int,Int))
colourAndCoords ( P c (x,y) ) = (c,(x,y))
I then created a function
coords :: [Point] -> [(Int,Int)]
coords ps = map snd (map colourAndCoords ps)
My thought was to compare this list to another list of all possible coordinations and where there was a match add the right colour, and where there wasn't I would add Nothing. However, my teacher said I was making it too complicated and that I should think of another solution. But I am having a hard time thinking of one. So I guess my question is what is the easier way? I am not asking for a solution, just a nudge in the right direction.
Thanks a ton for anyone taking the time to read this and respond!!
If I come up with a solution I will come back and update this thread.
If you want an efficient solution, the accumArray function does almost exactly what you need (after computing the appropriate bounds).
λ> arr = accumArray (const Just) Nothing ((0, 0), (2, 1)) [((y, x), c) | P c (x, y) <- lShape]
λ> arr
array ((0,0),(2,1)) [((0,0),Just Red),((0,1),Nothing),((1,0),Just Red),((1,1),Nothing),((2,0),Just Red),((2,1),Just Red)]
λ> elems arr
[Just Red,Nothing,Just Red,Nothing,Just Red,Just Red]
Now the problem is reduced to split elements into groups.
λ> chunksOf 2 (elems arr)
[[Just Red,Nothing],[Just Red,Nothing],[Just Red,Just Red]]
For a real application you’d probably want to leave it as an array, since array indexing is fast (O(1)) and list indexing is slow (O(n)).
If efficiency is no concern, you could consider this approach:
Figure out the largest x coordinate (call it w), and the largest y coordinate (call it h).
Create a rectangular list of lists, length w by h, with a list comprehension or similar. At each position, look through the entire list of points for one with a matching position, and use its color if you find one (Nothing otherwise).
If efficiency is a concern, you could consider a more complicated approach:
Turn your [AltShape] into an IntMap (IntMap Colour), e.g. using this technique, which maps y and x to colour.
Iterate over occupied rows with toAscList; within each, iterate over occupied columns with toAscList. You will need to manually pad unoccupied rows with [] and unoccupied columns with Nothing.
An advantage (or possibly disadvantage, depending on your goals!) of the second approach is that it will naturally produce "ragged" Shapes which omit trailing Nothings.
So I got a score list and want to create a ranking list from it. If scores are the same they share a rank.
For example if I have a score list like
[100, 100, 50, 50, 20]
the generated list would be
[(100, 1), (100, 1), (50, 2), (50, 2), (20, 3)]
I guess this is a fairly simple task, but I haven't gotten to solve it yet. I tried to do it with pattern matching or folding but without any luck.
My last failed approach looks like this:
scores = [100, 100, 50, 50, 20, 10]
ranks = foldr (\x acc -> if x == (fst $ last acc)
then last acc:acc
else (x, (+1) $ snd $ last acc):acc) [(head scores, 1)] scores
Any help is appreciated.
This solution is fairly similar to Willem's, except that it doesn't explicitly use recursion. Many style guides, including the Haskell wiki, suggest to avoid explicit recursion if there's a simple implementation involving higher-order functions. In your case, your function is a pretty straightforward use of scanl, which folds a list with an accumulating value (in your case, the accumulator is the current rank and score) and stores the intermediate results.
ranks :: Eq a => [a] -> [(a, Int)]
-- Handle the empty case trivially.
ranks [] = []
-- Scan left-to-right. The first element of the result should always
-- have rank 1, hence the `(x, 1)' for the starting conditions.
ranks (x:xs) = scanl go (x, 1) xs
-- The actual recursion is handled by `scanl'. `go' just
-- handles each specific iteration.
where go (curr, rank) y
-- If the "current" score equals the next element,
-- don't change the rank.
| curr == y = (curr, rank)
-- If they're not equal, increment the rank and
-- move on.
| otherwise = (y, rank + 1)
By avoiding explicit recursion, it's arguably easier to see at a glance what the function does. I can look at this, immediately see the scanl, and know that the function will be iterating over the list left-to-right with some state (the rank) and producing intermediate results.
We can write a recursive algorithm that maintains a state: the current rank it is assigning. The algorithm each time looks two elements far. In case the next element is the same, the rank is not incremented, otherwise it is.
We thus can implement it like:
rank :: Eq a => [a] -> [(a, Int)]
rank = go 1
where go i (x:xr#(x2:_)) = (x, i) : go'
where go' | x == x2 = go i xr
| otherwise = go (i+1) xr
go i [x] = [(x, i)]
go _ [] = []
We thus specify that rank = go 1, we thus "initialize" a state with 1. Each time we check with go if the list contains at least two elements. If that is the case, we first emit the first element with the state (x, i), and then we perform recursion on the rest xr. Depending on whether the first element x is equal to the second element x2, we do or do not increment the state. In case the list only contains one element x, we thus return [(x, i)], and in case the list contains no elements at all, we return the empty list.
Note that this assumes that the scores are already in descending order (or in an order from "best" to "worst", since in some games the "score" is sometimes a negative thing). We can however use a sort step as pre-processing if that would not be the case.
Here's a simple one-liner, putting some off-the-shelf pieces together with a list comprehension.
import Data.List
import Data.Ord (Down (..))
rank :: Ord a => [a] -> [(a, Int)]
rank xs = [(a, i) | (i, as) <- zip [1..] . group . sortBy (comparing Down) $ xs
, a <- as]
If the list is already sorted in reverse order, you can leave out the sortBy (comparing Down).
I've been struggling with foldr for a while now. i want to convert a list of ints to a single tuple that contains the sum of list and number of ints. for example [1,2,3,4] -> (10,4)
the function i have below iterates through the list fine but only outputs the last element as the x val and 1 as the y val.
toTuple xs = let tuple = foldl (\a b -> ((sum1 + b),(len1 + 1))) (0.0,0.0) xs
in tuple
where sum1 = 0
len1 = 0
i originally had sum1 and len1 as functions that take in an single int as an input but that didnt' work either so i changed them to variables initailized to 0. however it seems like its setting sum and len to 0 at every element in the list. any suggestions to modify this? thanks!
It looks like you haven’t built an intuition for folds yet. You can think of a fold like foldl combine start input as starting with some start value, then combining that value with each element of the input using the combine function. The function accepts the current “state” and the next element of the list, and returns the updated state. So you are very close to a working solution:
toTuple xs = foldl (\ (sum, len) x -> (sum + x, len + 1)) (0, 0) xs
Stepping through an example input like [1, 2, 3, 4], the state (sum, len), usually called an “accumulator”, takes on the following values each time the folding function is called:
(0, 0)
(0+1, 0+1) = (1, 1)
(0+1+2, 0+1+1) = (3, 2)
(0+1+2+3, 0+1+1+1) = (6, 3)
(0+1+2+3+4, 0+1+1+1+1) = (10, 4)
At no point are we modifying any variables, just calculating the next value of the sum and the length by combining the current partial sum & length with each element of the list. For a left fold (foldl) that’s done from left to right; for a right fold (foldr) it’s from right to left, so the order of the parameters ((sum, len) and x) is reversed:
toTuple xs = foldr (\ x (sum, len) -> (sum + x, len + 1)) (0, 0) xs
However, for right folds, I find it easier to think of them as replacing all the : in a list with a function and replacing the [] with a value:
foldr f z [1, 2, 3, 4]
foldr f z (1 : (2 : (3 : (4 : []))))
1 `f` (2 `f` (3 `f` (4 `f` z)))
Well foldl has type foldl :: (a -> b -> a) -> a -> [b] -> a with a the type of the accumulator (here a 2-tuple), and b the type of elements in the list.
But in your lambda expression you write
\a b -> ((sum1 + b),(len1 + 1))
Note that here the variable b is an element of the list, and a is a tuple. But here you omit the accumulator. You always add sum1 to b, but since sum1, is a constant, that is rather useless. The same for len1, as a result, you will always obtain a tuple that contains the last element of the list as first item, and 1 as second item.
But based on your attempt, I think that you somehow aim to write "imperative" code in Haskell. Now in Haskell all variables are immutable, so setting len1 to 0 (in the where clause), will result in the fact that len1 is always 0, etc. We do not change the accumulator, in fact foldr is a recursive function that each time calls itself with parameters with different values, and at the end returns the parameter we call the "accumulator".
We can thus change the attempt to:
toTuple = (Fractional s, Fractional n) => [s] -> (s, n)
toTuple = foldl (\(cursum, curlen) b -> (cursum + b, curlen + 1)) (0.0,0.0)
So here we each time pattern match the accumulator with (cursum, curlen) (containing the "current" (thus far) sum and length), and each time we construct a new tuple that contains the "new" current sum (the sum of the old sum and b), and the "new" current length (the length incremented with one).
But it it still not very elegant: here the initial tuple has value (0.0, 0.0), but as a result, you tell Haskell that this is a 2-tuple where both elements are Fractionals. Although this will probably work given there are no rounding errors, why restrict this to Fractionals? If you have a list of Integers for example, we can sum these up without any rounding errors. By using (0, 0) as initial tuple, we make the tuple a 2-tuple where both elements have a type that belongs to the Num typeclass: so numbers. Note that the two do not per se have the same Num type, which is less restrictive.
So now we got:
toTuple = (Num s, Num n) => [s] -> (s, n)
toTuple = foldl (\(cursum, curlen) b -> (cursum + b, curlen + 1)) (0, 0)