1. 19
  1.  

  2. 1

    Small Remark: Personally I would move the chaining of Transforms to within the data type (so add | Chain [Transform] to Transform). I think this would improve composability of transformations and also remove the need for the transform-function since in toMatrix and toCodeWorld you could then just directly pattern match on the Transform-type.

    Other than that a very nice article showing a nice use case of why to prefer data over functions.

    1. 1

      I’m unsure exactly what you’re suggesting. Can you please elaborate, perhaps by providing a full definition of the Transform type that you had in mind?

      1. 2

        Something like:

        data Transform
          = Scale Double Double
          | Translate Double Double
          | Chain [Transform]
        

        Then the function toMatrix would for example become:

        toMatrix :: Transform -> M33 Double
        toMatrix (Scale x y) = V3
          (V3 x 0 0)
          (V3 0 y 0)
          (V3 0 0 1)
        toMatrix (Translate x y) = V3
          (V3 1 0 x)
          (V3 0 1 y)
          (V3 0 0 1)
        toMatrix (Chain ts) =
          foldr ((!*!) . toMatrix) identity ts
        
        1. 1

          Thanks, but I disagree with this, for a couple of reasons: first, it makes Transform into an N-ary tree with leaves of Scale and Translate, but a tree doesn’t match the thinking we’re trying to capture: a linear sequence of transformations that can be applied to something later. Second, you lose the nice connection to the free monoid, which means that concerns about structuring multiple transformations are now intermingled with the specification of a single transformation.

          If the calls to transform are too much syntactic noise, then I’d rather write this:

          toMatrix :: [Transform] -> M33 Double
          toMatrix = foldr ((!*!) . toMatrix') identity
            where
              toMatrix' (Scale x y) = V3
                (V3 x 0 0)
                (V3 0 y 0)
                (V3 0 0 1)
              toMatrix' (Translate x y) = V3
                (V3 1 0 x)
                (V3 0 1 y)
                (V3 0 0 1)