How to build a DSL for looking up fields from a record in Haskell - haskell

TL;DR: I need help figuring out how to generate code that will return one of a small number of data types (probably just Double and Bool) from various fields on disparate records.
Long form: Assuming the following data types
data Circle = Circle { radius :: Integer, origin :: Point }
data Square = Square { side :: Integer }
and some boilerplate code
circle = Circle 3 (Point 0 0)
square = Square 5
I'm building a small DSL, and want the user to be write something like the following
circle.origin
square.side
and it will generate code similar to
origin . circle
side . square
In parsing this, I would have the strings "circle" and "origin" for example. I now need to turn those into function calls. I could obviously have something like this:
data Expr a = IntegerE (a -> Integer)
| PointE (a -> Point)
lookupF2I "side" = Just $ IntegerE side
lookupF2I "radius" = Just $ IntegerE radius
lookupF2I _ = Nothing
lookupF2P "origin" = Just $ PointE origin
lookupF2P _ = Nothing
and have one lookup function per returned data type. Having one function per data type is practical from the DSL point of view in that it will only really deal with 2 or 3 data types. However, this hardly seems like a particularly effective way of doing things. Is there a better way (surely) of doing this? If not, is there a way that I can generate the code for the various lookup functions from the various records that I want to be able to lookup fields from?
Secondly, there's still the matter of the parsed "circle" or "square" needing to call the appropriate circle or square function. If I were to implement this using type classes, I could do something like:
instance Lookup Circle where
lookupF2I "radius" = Just $ IntegerE radius
lookupF2I _ = Nothing
lookupF2P "origin" = Just $ PointE origin
lookupF2P _ = Nothing
but then that leaves me with having to figure out which type to enforce on the lookup function, and worse having to hand write instances for each (of many) records that I want to use this on.
Note: The fact that Circle and Square could be represented using a single ADT is incidental to my question in that this is a contrived example. The actual code will entail various very different records, of which the only thing they have in common is having fields of the same type.

I tried using Template Haskell to provide a nice and type safe way to solve this problem. To do this, I constructed the expressions from a given string.
I suppose that Lens package can do that, but it may be a simpler and more flexible solution.
It can be used like this:
import THRecSyntax
circleOrigin = compDSL "circle.origin.x"
And is defined like this:
{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
compDSL :: String -> Q Exp
compDSL s = return
$ foldr1 AppE
$ map (VarE . mkName)
(reverse $ splitEvery '.' s)
So the result expression will be: x (origin circle)
Note: splitEvery is a function that splits a list into sublists taking out the given element. Example implementation:
splitEvery :: Eq a => a -> [a] -> [[a]]
splitEvery elem s = splitter (s,[])
where splitter (rest, res) = case elemIndex elem rest of
Just dotInd -> let (fst,rest') = splitAt dotInd rest
in splitter (tail rest', fst : res)
Nothing -> reverse (rest : res)
This is a heavyweight but type-safe way to create an embedded DSL with the given syntax.

Related

Creating list of values of the same typeclass but different types

I'm new to Haskell and trying to do something which I'm sure is easy but I'm not seeing the right way to do it.
What I want is a list of values of a particular typeclass, but different types of that typeclass. Eg:
class Shape a where
area :: a -> Double
numVertices :: a -> Integer
data Triangle = Triangle {...}
data Square = Square {...}
instance Shape Triangle where ...
instance Shape Square where ...
x = [Triangle (...), Square (...)]
I'm getting a compiler error because the list has different types. What's the right way to do what I'm trying to do here? The only thing I've been able to come up with is doing something like this:
data WrappedShape = WrappedShape {
getArea :: () -> Double
, getNumVertices :: () -> Integer
}
wrap s = WrappedShape {
getArea = \ () -> area s
, getNumVertices = \ () -> vertices s
}
x = [wrap (Triangle (...)), wrap (Square (...))]
This works, but it's heavy on boilerplate, since I have to effectively define Shape twice and with differently-named members. What's the standard way to do this sort of thing?
If you just need a few different shapes, you can enumerate each shape as a constructor, here is a example:
data SomeShapes = Triangle {...}
| Square {...}
instance Shape SomeShapes where
area (Triangle x) = ...
area (Square x) = ....
now you can put them in a list, because they are same type of SomeShapes
[Triangle {...}, Square {...}]
Your wrapped type is probably the best idea.
It can be improved by noting that, in a lazy language like Haskell, the type () -> T essentially works like the plain T. You probably want to delay computation and write stuff like let f = \ () -> 1+2 which does not perform addition until the function f is called with argument (). However, let f = 1+2 already does not perform addition until f is really needed by some other expression -- this is laziness.
So, we can simply use
data WrappedShape = WrappedShape {
getArea :: Double
, getNumVertices :: Integer
}
wrap s = WrappedShape {
getArea = area s
, getNumVertices = vertices s
}
x = [wrap (Triangle (...)), wrap (Square (...))]
and forget about passing () later on: when we will access a list element, the area/vertices will be computed (whatever we need). That is print (getArea (head x)) will compute the area of the triangle.
The \ () -> ... trick is indeed needed in eager languages, but in Haskell it is an anti-pattern. Roughly, in Haskell everything has a \ () -> ... on top, roughly speaking, s o there's no need to add another one.
These is another solution to your problem, which is called an "existential type". However, this sometimes turns into an anti-pattern as well, so I do not recommend to use it lightly.
It would work as follows
data WrappedShape = forall a. Shape a => WrappedShape a
x = [WrappedShape (Triangle ...), WrappedShape (Square ...)]
exampleUsage = case head x of WrappedShape s -> area s
This is more convenient when the type class has a lots of methods, since we do not have to write a lot of fields in the wrapped type.
The main downside of this technique is that it involves more complex type machinery, for no real gain. I mean a basic list [(Double, Integer)] has the same functionality of [WrappedShape] (list of existentials), so why bother with the latter?
Luke Palmer wrote about this anti-pattern. I do not agree with that post completely, but I think he does have some good points.
I do not have a clear-cut line where I would start using existentials over the basic approach, but these factors are what I would consider:
How many methods does the type class have?
Are there any methods of the type class where the type a (the one related to the class) appears not only as an argument? E.g. a method foo :: a -> (String, a)?

"For all" statements in Haskell

I'm building comfort going through some Haskell toy problems and I've written the following speck of code
multipOf :: [a] -> (Int, a)
multipOf x = (length x, head x)
gmcompress x = (map multipOf).group $ x
which successfully preforms the following operation
gmcompress [1,1,1,1,2,2,2,3] = [(4,1),(3,2),(1,3)]
Now I want this function to instead of telling me that an element of the set had multiplicity 1, to just leave it alone. So to give the result [(4,1),(3,2),3] instead. It be great if there were a way to say (either during or after turning the list into one of pairs) for all elements of multiplicity 1, leave as just an element; else, pair. My initial, naive, thought was to do the following.
multipOf :: [a] -> (Int, a)
multipOf x = if length x = 1 then head x else (length x, head x)
gmcompress x = (map multipOf).group $ x
BUT this doesn't work. I think because the then and else clauses have different types, and unfortunately you can't piece-wise define the (co)domain of your functions. How might I go about getting past this issue?
BUT this doesn't work. I think because the then and else clauses have different types, and unfortunately you can't piece-wise define the (co)domain of your functions. How might I go about getting past this issue?
Your diagnosis is right; the then and else must have the same type. There's no "getting past this issue," strictly speaking. Whatever solution you adopt has to use same type in both branches of the conditional. One way would be to design a custom data type that encodes the possibilities that you want, and use that instead. Something like this would work:
-- | A 'Run' of #a# is either 'One' #a# or 'Many' of them (with the number
-- as an argument to the 'Many' constructor).
data Run a = One a | Many Int a
But to tell you the truth, I don't think this would really gain you anything. I'd stick to the (Int, a) encoding rather than going to this Run type.

How to read settings and geometric shapes from a file in Haskell for later use, with operations in between?

I can write simple algorithms in Haskell, and even successfully coded a very simple graphics raytracer (spheres, planes, rectangles) as a learning tool (I wrote a more complex one in C++, for an online course). All settings for this raytracer were hardcoded as constants, be it the desired image width/height, camera position, ambient light intensity, etc. Example:
imageWidth = 1600
imageHeight = 900
bgColor = Color 0 0 0
maxDepth = 5
ambientIntensity = Color 0 0 0
However, as soon as I tried to extend the raytracer to read these settings and the scene itself (positions of objects, lights, etc) from a file, I hit a brick wall. Example scene file:
size 1600 900
output generated_image.png
ambient 0.1 0.1 0.1
triangle 0.5 1.4 2.8
triangle 0.5 2.4 3.8
sphere -5.5 -5.5 0 4
sphere +5.5 +5.5 0 4
Important: The scene file additionally includes matrix operations (translate, rotate, etc), which I should store in a stack as I read the file, as well as material definitions for the objects. If I read a sphere in a certain line, that sphere should use the material and matrix transformation that are set as of that line. Then, some more matrix transformations and material settings may or may not follow before reading another object (sphere, triangle, etc), and so on.
It seems to me that this task involves some severe data mutation. Due to Haskell's pure nature and my limited knowledge, I'm having problems with IO types and how to proceed in general, and my Internet research honestly didn't help a lot.
I know how to read the scene file using readFile, get each line using lines, separate parameters using words and even convert those to Ints/Floats as needed using read. I believe I should apply a function to each line, probably using mapM or mapM_, which should detect the used command (size, ambient, sphere, etc), and act as needed. But actions are very different for each command. While "size" only requires that I save the image width and height in values, "sphere" would require that I read values, use the currently active matrix transformation, currently active material and then store it in a list somewhere. I can't read everything and then act, or I would have to also store the order of operations and the problem would be the same... But, even in the simpler "size" case, I'm clueless about how to do this, as these are all operations that involve mutation.
Concretely:
How should I go about binding a value read from a file to a name, in order to use it later? Ex.: imageWidth and imageHeight. With only one setting in the file, I could do this by simply returning the read value from the reader function. This is not the case...
Should I create a new data type named "Scene" with named parameters (they are many), which contains all the settings to later use in the raytracer? This is how I would do it in C++, but here it pollutes the function namespace (if that is how I should call it) with all the arguments.
How could I achieve this mutation of values? I'm assuming I need pointers or some impure Haskell functionality, and I believe only this initial setup would require such things. Later on, when the image is generated, I should be able to access the stored values as usual, using pure functions. Is this possible? None of the settings read from the file are supposed to change in runtime, but they involve "mutation" of data while reading, especially in the case of materials, the stack of matrix transformations and even adding to the list of objects.
I apologize for the long question. I realize it is also rather abstract and hasn't got a "code solution". If my questions are too broad, could you recommend a resource where such a problem is tackled in a clear way? I'm feeling that I also need to learn quite a lot about Haskell before achieving this.
Many thanks.
It seems now this question is simply about parsing your particular file format. So I will show you how to use a commonly used parsing library, Parsec, to do it. If you are not familiar with parsec and applicative style parsing, please read the section in RWH. This will essentially be a fully featured parser, so it is quite long.
I will repeat it once more: using mutation to do this in Haskell is simply wrong. Only a masochist would even attempt it. Please push all ideas of mutation out of your mind.
First, write datatypes to represent everything:
type Point3D = (Float, Float, Float)
data SceneObject
= Sphere Point3D Float
| Triangle Point3D Point3D Point3D
deriving Show
data SceneTransform
= Translate Float Float Float
| Rotate Float Float Float
deriving Show
Notice we seperate things into transformations and objects. The distinction, in general, is that transformations are things which can be applied to objects. Then, the entire scene:
data SceneConfig x = SceneConfig
{ sc_height :: Int
, sc_width :: Int
, sc_out :: FilePath
, sc_objs :: x
} deriving Show
Notice the objects are a parameter. This is because we will first parse the data exactly as it is found in the file, then write a function which will transform the data to a more convenient format. We will not do something absurd like trying to parse files and transform the parsed data simultaneously.
{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}
import Text.Parsec hiding ((<|>))
import Text.ParserCombinators.Parsec.Number
import Control.Applicative hiding (many)
type Parser = Parsec String ()
parseFloat :: Parser Float
parseFloat = spaces >> (sign <*> (either fromInteger id <$> decimalFloat))
parsePoint3D :: Parser Point3D
parsePoint3D = spaces >> ((,,) <$> parseFloat <*> parseFloat <*> parseFloat)
These are helper functions for parsing basic things. We parse points as floats separated by whitespace.
parseTranslate =
string "translate" >> Translate <$> parseFloat <*> parseFloat <*> parseFloat
The above is quite simple: a translate object is the string "Translate" followed by three floats.
The other possible objects look pretty much exactly the same:
parseRotate =
string "rotate" >> Rotate <$> parseFloat <*> parseFloat <*> parseFloat
parseSphere =
string "sphere" >> Sphere <$> parsePoint3D <*> parseFloat
parseTriangle =
string "triangle" >> Triangle <$> parsePoint3D <*> parsePoint3D <*> parsePoint3D
We need a parser which parses any of these. choice takes a list of parsers and succeeds on the first one of them which succeeds:
parseObjOrTransform :: Parser (Either SceneObject SceneTransform)
parseObjOrTransform = choice $ map try $
[ Left <$> parseSphere
, Left <$> parseTriangle
, Right <$> parseRotate
, Right <$> parseTranslate
]
Now we are ready to parse the entire config:
parseSceneConfigWith :: Parser x -> Parser (SceneConfig x)
parseSceneConfigWith p = do
string "size"
sc_height <- spaces >> int
sc_width <- spaces >> int
char '\n'
string "output"
sc_out <- spaces >> many1 (noneOf "\n\t\"<>|/\\?*: ")
char '\n'
sc_objs <- p
return $ SceneConfig { .. }
This requires that "size" and "output" are placed in the correct order. You can, of course, change this; but this way is the simplest.
Now we parse the data including objects and transformations - but again, we do not do computation on them while parsing:
parseSceneRaw :: Parser (SceneConfig [Either SceneObject SceneTransform])
parseSceneRaw = parseSceneConfigWith (sepEndBy parseObjOrTransform (char '\n'))
Now we are ready to apply transforms to objects:
appTr :: SceneTransform -> SceneObject -> SceneObject
appTr (Translate dx dy dz) obj =
case obj of
(Sphere p0 r) -> Sphere (dp ~+~ p0) r
(Triangle p0 p1 p2) -> Triangle (dp ~+~ p0) (dp ~+~ p1) (dp ~+~ p2)
where dp = (dx, dy, dz)
appTr _ _ = error "TODO"
applyTransforms :: [Either SceneObject SceneTransform] -> [SceneObject]
applyTransforms [] = []
applyTransforms (Left obj : xs) = obj : applyTransforms xs
applyTransforms (Right tf : xs) = applyTransforms (map f xs) where
f (Left obj) = Left $ appTr tf obj
f x = x
The logic of this function is fairly simple. It applies each transform it encounters to every subsequent object. You could do this with a matrix stack, but it is overkill, at least for the subset of your datatype I have implemented.
Then, for convenience, we can write a parser which performs parseSceneRaw, then applies the transforms:
parseScene :: Parser (SceneConfig [SceneObject])
parseScene = do
SceneConfig { sc_objs, .. } <- parseSceneRaw
return $ SceneConfig { sc_objs = applyTransforms sc_objs, .. }
Then a simple test case:
testFile :: String
testFile = unlines
["size 1600 900"
,"output generated_image.png"
,"translate 0 1 0"
,"triangle 0.5 1.4 2.8 4.5 2.3 3.1 9.6 1.4 0.0"
,"translate 10 10 10"
,"sphere -5.5 -5.5 0 4"
,"translate -100 -100 -100"
,"sphere 5.5 5.5 0 4"
]
testMain = print (parse parseSceneRaw "" testFile) >>
print (parse parseScene "" testFile)
If you are willing to change your file format, one low-effort option would be to create a data structure for the commands, that implements Show and Read. Then you can use these to read the data structure from disk all at once, and to write it out again if need be.
For instance, I have a program with the following data structure declared below. The SoundMap contains within it a list of SoundSets, and those SoundSets may each be one of the three types. In your case you'd have a list of Commands of various types.
data SoundSet =
Synth {
syn_name :: String,
syn_keytype :: KeyType } |
NoteWavSet {
nws_rootdir :: T.Text,
nws_denominator :: Integer,
nws_notemap :: [(Integer, T.Text, KeyType)]
} |
KeyWavSet {
kws_rootdir :: T.Text,
kws_wavs :: [(T.Text, KeyType)]
}
deriving (Show, Read)
data SoundMap = SoundMap {
sm_soundsets :: [(T.Text, SoundSet)],
sm_keymaps :: [[(KeyRange, T.Text)]]
}
deriving (Show, Read)
The data structure is read in like so:
sml_str <- readFile (args !! 2)
let smap = read sml_str :: SoundMap in
<moar code>
And for sanity's sake, its written out with ppShow:
writeFile (args !! 3) $ ppShow $ SoundMap {
sm_soundsets = [(wavsetname, ws)],
sm_keymaps = [[(All, wavsetname)]] }
The only caveat here is that the Read parser is sensitive to syntax errors, and is not helpful in finding them.

Storing values in a data structure Haskell

I'm trying to store randomly generated dice values in some data structure, but don't know how exactly to do it in Haskell. I have so far, only been able to generate random ints, but I want to be able to compare them to the corresponding color values and store the colors instead (can't really conceive what the function would look like). Here is the code I have --
module Main where
import System.IO
import System.Random
import Data.List
diceColor = [("Black",1),("Green",2),("Purple",3),("Red",4),("White",5),("Yellow",6)]
diceRoll = []
rand :: Int -> [Int] -> IO ()
rand n rlst = do
num <- randomRIO (1::Int, 6)
if n == 0
then printList rlst -- here is where I need to do something to store the values
else rand (n-1) (num:rlst)
printList x = putStrLn (show (sort x))
--matchColor x = doSomething()
main :: IO ()
main = do
--hSetBuffering stdin LineBuffering
putStrLn "roll, keep, score?"
cmd <- getLine
doYahtzee cmd
--rand (read cmd) []
doYahtzee :: String -> IO ()
doYahtzee cmd = do
if cmd == "roll"
then do rand 5 []
else putStrLn "Whatever"
After this, I want to be able to give the user the ability to keep identical dices (as in accumulate points for it) and give them a choice to re-roll the left over dices - I'm thinking this can done by traversing the data structure (with the dice values) and counting the repeating dices as points and storing them in yet another data structure. If the user chooses to re-roll he must be able to call random again and replace values in the original data structure.
I'm coming from an OOP background and Haskell is new territory for me. Help is much appreciated.
So, several questions, lets take them one by one :
First : How to generate something else than integers with the functions from System.Random (which is a slow generator, but for your application, performance isn't vital).
There is several approaches, with your list, you would have to write a function intToColor :
intToColor :: Int -> String
intToColor n = head . filter (\p -> snd p == n) $ [("Black",1),("Green",2),("Purple",3),("Red",4),("White",5),("Yellow",6)]
Not really nice. Though you could do better if you wrote the pair in the (key, value) order instead since there's a little bit of support for "association list" in Data.List with the lookup function :
intToColor n = fromJust . lookup n $ [(1,"Black"),(2,"Green"),(3,"Purple"),(4,"Red"),(5,"White"),(6,"Yellow")]
Or of course you could just forget this business of Int key from 1 to 6 in a list since lists are already indexed by Int :
intToColor n = ["Black","Green","Purple","Red","White","Yellow"] !! n
(note that this function is a bit different since intToColor 0 is "Black" now rather than intToColor 1, but this is not really important given your objective, if it really shock you, you can write "!! (n-1)" instead)
But since your colors are not really Strings and more like symbols, you should probably create a Color type :
data Color = Black | Green | Purple | Red | White | Yellow deriving (Eq, Ord, Show, Read, Enum)
So now Black is a value of type Color, you can use it anywhere in your program (and GHC will protest if you write Blak) and thanks to the magic of automatic derivation, you can compare Color values, or show them, or use toEnum to convert an Int into a Color !
So now you can write :
randColorIO :: IO Color
randColorIO = do
n <- randomRIO (0,5)
return (toEnum n)
Second, you want to store dice values (colors) in a data structure and give the option to keep identical throws. So first you should stock the results of several throws, given the maximum number of simultaneous throws (5) and the complexity of your data, a simple list is plenty and given the number of functions to handle lists in Haskell, it is the good choice.
So you want to throws several dices :
nThrows :: Int -> IO [Color]
nThrows 0 = return []
nThrows n = do
c <- randColorIO
rest <- nThrows (n-1)
return (c : rest)
That's a good first approach, that's what you do, more or less, except you use if instead of pattern matching and you have an explicit accumulator argument (were you going for a tail recursion ?), not really better except for strict accumulator (Int rather than lists).
Of course, Haskell promotes higher-order functions rather than direct recursion, so let's see the combinators, searching "Int -> IO a -> IO [a]" with Hoogle gives you :
replicateM :: Monad m => Int -> m a -> m [a]
Which does exactly what you want :
nThrows n = replicateM n randColorIO
(I'm not sure I would even write this as a function since I find the explicit expression clearer and almost as short)
Once you have the results of the throws, you should check which are identical, I propose you look at sort, group, map and length to achieve this objective (transforming your list of results in a list of list of identical results, not the most efficient of data structure but at this scale, the most appropriate choice). Then keeping the colors you got several time is just a matter of using filter.
Then you should write some more functions to handle interaction and scoring :
type Score = Int
yahtzee :: IO Score
yahtzeeStep :: Int -> [[Color]] -> IO [[Color]] -- recursive
scoring :: [[Color]] -> Score
So I recommend to keep and transmit a [[Color]] to keeps track of what was put aside. This should be enough for your needs.
You are basically asking two different questions here. The first question can be answered with a function like getColor n = fst . head $ filter (\x -> snd x == n) diceColor.
Your second question, however, is much more interesting. You can't replace elements. You need a function that can call itself recursively, and this function will be driving your game. It needs to accept as parameters the current score and the list of kept dice. On entry the score will be zero and the kept dice list will be empty. It will then roll as many dice as needed to fill the list (I'm not familiar with the rules of Yahtzee), output it to the user, and ask for choice. If the user chooses to end the game, the function returns the score. If he chooses to keep some dice, the function calls itself with the current score and the list of kept dice. So, to sum it up, playGame :: Score -> [Dice] -> IO Score.
Disclaimer: I am, too, very much a beginner in Haskell.
at first thought:
rand :: Int -> IO [Int]
rand n = mapM id (take n (repeat (randomRIO (1::Int, 6))))
although the haskellers could remove the parens

Good functions and techniques for dealing with haskell tuples?

I've been doing a lot of work with tuples and lists of tuples recently and I've been wondering if I'm being sensible.
Things feel awkward and clunky which for me signals that I'm doing something wrong.
For example I've written three convenience functions for getting the first, second and third value in a tuple of 3 values.
Is there a better way I'm missing?
Are there more general functions that allow you to compose and manipulate tuple data?
Here are some things I am trying to do that feel should be generalisable.
Extracting values:
Do I need to create a version of fst,snd,etc... for tuples of size two, three, four and five, etc...?
fst3(x,_,_) = x
fst4(x,_,_,_) = x
Manipulating values:
Can you increment the last value of every tuple in a list of pairs and then use that same function to increment the last value of every tuple in a list of triples?
Zipping and Unzipping values:
There is a zip and a zip3. Do I also need a zip4? or is there some way of creating a general zip function?
Sorry if this seems subjective, I honestly don't know if this is even possible or if I'm wasting my time writing 3 extra functions every time I need a general solution.
Thank you for any help you can give!
Extracting values
Yes, you need to write fstN yourself. But why not extract it in pattern matching?
Zipping and Unzipping values
Data.List already provides up to zip7. For general zipN, use a ZipList.
See How to zip multiple lists in Haskell?.
Manipulating values
Not without extensions. Since all tuples are of different types, you have to create a type class, for example:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}
class Firstable a b c | a -> b, a -> c where
firstOf :: a -> b
restOf :: a -> c
concatenate :: b -> c -> a
instance Firstable [a] a [a] where
firstOf = head
restOf = tail
concatenate = (:)
instance Firstable (a,b) a b where
firstOf = fst
restOf = snd
concatenate = (,)
instance Firstable (a,b,c) a (b,c) where
firstOf (x,_,_) = x
restOf (_,x,y) = (x,y)
concatenate x (y,z) = (x,y,z)
instance Firstable (a,b,c,d) a (b,c,d) where
firstOf (x,_,_,_) = x
restOf (_,x,y,z) = (x,y,z)
concatenate x (y,z,w) = (x,y,z,w)
instance Firstable (a,b,c,d,e) a (b,c,d,e) where
firstOf (x,_,_,_,_) = x
restOf (_,x,y,z,w) = (x,y,z,w)
concatenate x (y,z,w,t) = (x,y,z,w,t)
Then you could use
incFirst :: (Num b, Firstable a b c) => a -> a
incFirst x = (1 + firstOf x) `concatenate` restOf x
main = do
print $ map incFirst [(1,2),(3,4),(5,6)]
print $ map incFirst [(1,3,6,7),(2,5,-2,4)]
(lastOf is similar.)
But why not use separate functions?
When I start to have big tuples, I use Haskell's pitiful excuse for record syntax to give each element a name, e.g.,
data LatticeOperations a = LO { bot :: a
, top :: a
, glb :: a
, lub :: a
, le :: a
}
This is a five-tuple, but the names turn into functions that select the individual elements.
For changing tuples, you have record-update syntax. In the example I've just given, it makes no sense to replace just one element, but I might, for example, refine the partial order and replace three elements
lattice { le = le', glb = glb', lub = lub' }
And of course if you have a big record and are just trying to increment you can do something like
data FatRecord = FR { count :: Int, ... }
fat = fat { count = count fat + 1 }
I don't think record syntax helps with zip and unzip.
Once tuples get more than size 3 or so, and/or the same tuple type gets used widely, it's best to use a record.
There is no way to generalise over tuple-size without using extensions like Template Haskell. So if we just consider plain haskell: Yes, you do need to write versions of fst etc. for each tuple-size and no, you can't write a general zip method.
I recently found that
{-# LANGUAGE RecordWildCards #-}
allows for a record to use the field selectors as values -
like this:
data R = R {a::Int, b::String, c::Bool}
testA R{..} = a > 0
or
.. = R {..} where a = 7; b= "x"; c= True
check the GHC manual!
Better late than never: try the Tuple package.
Haskell's built-in fst and snd only support tuple pairs, so you're right to define your own. If you want to increment the last value in a list, reverse the list, work from that end, and reverse it back. If you want that increment to work for lists and lists of tuples, just define a new function that increments for those data types and call that within your increment function for the list. #KennyTM has answered the zipN question.
Working with lists and tuples in Haskell is a little different than a lot of languages, but after a while of they feel especially natural and powerful in comparison.

Resources