6

I'm writing a raytracer in Haskell and, currently, I define my scene in code like so:

(Scene [(Sphere (Vec3 1 0 0) 4 (PhongMaterial (color 1 0 0) (color 1 1 1) 4))]
       [(PhongLight (Vec3 0 0 0) (color 1 1 1) (color 1 1 1))])

This works really well in terms of expressivity, and it's great because I don't have to write any sort of parser, but it means that I have to recompile every time I want to render a different scene. I've come to Haskell through Lisp, where this would be simple (load a file, eval the contents and then render the result) but I recognize that Haskell has traits that makes that, if not impossible, then very difficult.

Do any of you much more experienced Haskellers have any suggestions for the best way to go around solving this? In an ideal world, I'd have some file external to my code that defined the scene in Haskell syntax which I could load; in the least-ideal world possible I'd be writing a parser in Parsec. Thanks!

5
  • 3
    Can you say a bit more about the color function? Generally, Haskell has lots of good libraries for serializing and deserializing data in various ways, but serializing and deserializing code can be a bit more involved. If those calls to color can be replaced with the data they produce instead, that makes the situation much easier. Commented Oct 31, 2011 at 14:19
  • Is color 1 1 1 a function call? Could it be changed to a constructor (so that it's data)? Commented Oct 31, 2011 at 14:22
  • Do you want to interpret everything or do you want to compile the renderer and load the scene dynamically? Commented Oct 31, 2011 at 15:53
  • The color function is currently just a convenience function for a longer, more complex data constructor, but I'd be willing to sacrifice some of the succinctness for a simple solution. Commented Oct 31, 2011 at 19:29
  • @HeinrichApfelmus: I want to compile the renderer and load the scene dynamically from the renderer. Commented Oct 31, 2011 at 19:36

3 Answers 3

7

If you make sure all of your data are instances of Read (... deriving Read), then you can just read them :: Withatypeifnecessary.

An intermediate solution would be to use json; parsing is easier than using Parsec, but of course it's a bit harder than just read-ing in code.


Update: if there are non-constructor functions, the read approach won't work.

Sign up to request clarification or add additional context in comments.

2 Comments

What's your story for the color 1 0 0 bits, that actually are a call to a (non-constructor) function?
Two really good options. I think I'll go the Read route for now. Thank you!
1

In this case, could you simply use read (assuming that everything does deriving Read). It's obviously a little clunky but chances are it would probably work (and it's less work than going down the parsec route).

1 Comment

It does handle extra parentheses. Try it. =)
0

How about using a haskell interpreter like Hint? Admittedly this is not as easy as in lisp and you need to be extra careful that you compile your renderer with the same modules as you use for describing your scenes, but still it could work ok, even though it isn't really a Haskell way of doing things.

 main :: IO ()
 main = do 
      [a] <- getArgs
      r <- runInterpreter (loadScene a)
      case r of
        Left err -> printInterpreterError err
        Right scene -> raytrace scene

 loadScene :: Filepath -> Interpreter ()
 loadScene fn = do
      loadModules [fn]
      eval "name_of_variable_describing_scene_in_my_source"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.