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

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.

Related

QuickCheck limit to only certain data constructor

I have a data type definition:
data Point = Point {x :: Int, h :: Int} | EmptyPoint
In my property test, I would like to limit the test only on the Point constructor cases. For example point1 - point2 = Point 0 0. This presumes that the accessor x is defined which is not the case with EmptyPoint.
in other words: I don't want EmptyPoint to be generated.
Is there a way to do that?
Instead of automatically deriving the Arbitrary class for your type (which is what, I assume, you're doing at the moment), you can just write one manually and make it generate your points however you want, for example:
instance Arbitrary Point where
arbitrary = Point <$> arbitrary <*> arbitrary
Or in a slightly more verbose way if you like:
instance Arbitrary Point where
arbitrary = do
x <- arbitrary
y <- arbitrary
pure Point { x, y }

Haskell - Creating rectangle of 1s with height and width of user input

I am trying to create a function that will take two integer values that correspond to the width and height of a rectangle of 1s that is outputted like so:
Main> rectangle 3 4
1111
1111
1111
I am a beginner at Haskell and have no experience, so any pointers would be appreciated, and therefore only basic Haskell operations should be used. Thanks.
rectangle :: Int -> Int -> String
rectangle h w = unlines (replicate h (replicate w '1'))
Although this is a pure function, it does show the relevant part. You can just putStrLn (rectangle 3 4) to have it printed out as expected in ghci, rather than wrapped in a show.
Giving it a second thought, here's a short walkthrough.
replicate :: Int -> a -> [a]
unlines :: [String] -> String
As you can see, replicate w '1'creates a list of w times the charakter 1. Because String = [Char], the result is a String of ones, as many as w says.
Now, this String is replicated again, h times, giving a list of h times that string.
unlines now concatenates those strings, inserting a new line character between the strings.
The result is what you'd expect, only that ghci (which you appear to be using) is wrapping each expression's result in a show call. So, to do exactly what you want to achieve, a call to putStr in needed as so:
impureRectangle :: Int -> Int -> IO ()
impureRectangle x y = putStr (rectangle x y)
Note that monads (or IO, as the first monad, people use to get to know as such) are not the easiest things to get your head around. I'd suggest staying pure until you feel safe.

How should I organize normal and "state" versions of functions in the state monad?

I decided to try the State Monad to try and clean up some of the projects that I've started. I ran into a naming/compartmentalization problem.
If I have the following objects:
data Obj = Player { oPos :: Point }
data World = World { wKeys :: [Key], wPlayer :: Obj }
I might have a convenience function like:
setPlayer :: Obj -> World -> World
setPlayer o w = w{wPlayer = o}
and a matching state operation like:
setPlayerW :: Obj -> WorldState ()
setPlayerW o = get >>= put . setPlayer o
which uses the other convenience function; for convenience.
What is the typical naming convention for something like this? I post-pended the state version with a W, but that's kind of ugly.
And are the "state-versions" typical segregated from the "object-versions" in a separate file?
Am I going about this wrong completely? Is there a better set-up then having 2 different versions of any operations I might need?
Personally, I wouldn't have a separate function for the state version myself. Instead, use the modify function instead:
do let o = player
modify (setPlayer o)
something else
In a sense, the naming convention you're looking for would just be the same as using modify except folded into the names of each function. When I find myself naming functions like this, I generally try to find some way to organize them in the language instead of using their names. Sometimes, like here, an existing function is all you need; other times, it involves creating a function of your own to achieve the same end or extracting things into a module.
The core idea is that it's better to reify patterns in your code using first-class language constructs instead of encoding them indirectly into the names. (Of course, if this ends up really awkward, you shouldn't do it, but it's fine here.)
This looks like a good time to introduce the lens library. This is a bit of a daunting library at first, and I still struggle with some of its more complex features (the rabbit hole is pretty deep for this one), but it can really simplify a lot of your State code. To use it, it's recommended to change your data types a bit first so you can utilize template haskell:
import Control.Monad
import Control.Monad.State
import Control.Lens
-- Made assumption on what Point would look like
data Point = Point { _x :: Int, _y :: Int } deriving (Eq, Show)
data Obj = Player { _oPos :: Point } deriving (Eq, Show)
data World = World { _wKeys :: [Key], _wPlayer :: Obj } deriving (Eq, Show)
makeLenses ''Point
makeLenses ''Obj
makeLenses ''World
-- Also assumed this type
type WorldState = StateT World IO
Then you can write code that looks very much imperative using the generated lenses
setPlayerW :: Obj -> WorldState ()
setPlayerW o = wPlayer .= o
Or if you want the non-monadic version
setPlayer :: Obj -> World -> World
setPlayer o = wPlayer .~ o
So this uses the .= operator to set the wPlayer field of the World state to the new value. More impressively, you could use it to write code like
moveUp, moveDown, moveLeft, moveRight :: WorldState ()
moveUp = wPlayer.oPos.y += 1
moveDown = wPlayer.oPos.y -= 1
moveLeft = wPlayer.oPos.x -= 1
moveRight = wPlayer.oPos.x += 1
Which makes it look a lot like an object oriented language using normal function composition. A quick test:
game :: WorldState ()
game = do
replicateM_ 3 moveUp
replicateM_ 5 moveLeft
replicateM_ 10 moveRight
replicateM_ 6 moveDown
> execStateT game $ World [] $ Player $ Point 0 0
World {_wKeys = [], _wPlayer = Player {_oPos = Point {_x = 5, _y = -3}}}
There are a lot of really interesting and useful operators in the lens library, and there's a lot of support built-in for using it with StateT stacks.
Another nice feature is the zoom function, which takes a lens and "zooms in" on it, letting you operate as if your state has the value of whatever you zoomed onto. An example would be
game = zoom (wPlayer.oPos) $ do
y += 3
x -= 5
x += 10
y -= 6
And this would produce the same result as before. This is generally more efficient (fewer layers to unwrap at each step) and can be much cleaner.

How to build a DSL for looking up fields from a record in 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.

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

Resources