Haskell Diagrams alignment function

238 Views Asked by At

I'm trying use Haskell Diagrams to make my own function which places two diagrams horizontally/vertically adjacent to one another (like ||| or ===) but with a space between them. If I try to do like this, I get error: Illegal equational constraint V a ~ R2

emptyBlock = rect (3) (1) # fc white # lc white
(||||) :: (Juxtaposable a, V a ~ R2, Semigroup a) => a -> a -> a
(||||) = (|||) emptyBlock (|||)
(====) :: (Juxtaposable a, V a ~ R2, Semigroup a) => a -> a -> a
(====) = (===) emptyBlock (===)

I would be very thankful if anyone could help me to fix this problem.

1

There are 1 best solutions below

0
On

There are several problems.

  1. As the error your code gives says, you need to enable either the GADTs or TypeFamilies language extension to use type equality constraints (the ones that mention ~).
  2. Your syntax isn't quite right: (===) emptyBlock (===) is attempting to put the two "diagrams" emptyBlock and (===) above each other. Since (===) isn't a diagram, but rather a function that forms diagrams, this isn't going to fly. You should write

    x ==== y = x === emptyBlock === y
    

    instead.

  3. You claim that (||||) and (====) work for any juxtaposable diagrams, but its implementation includes a white rectangle, which means it has to be something that can be styled, has trails, and can be transformed. Change the type signature lines as follows:

    (||||), (====) :: (Juxtaposable a, V a ~ R2, Semigroup a, TrailLike a, HasStyle a, Transformable a) => a -> a -> a
    

    (Or, possibly, omit it entirely if you plan to disable the monomorphism restriction.)

  4. You haven't given emptyBlock a type signature, and it is typeclass polymorphic, so the monomorphism restriction kicks in. Give it a type signature, or turn off the monomorphism restriction by enabling the NoMonomorphismRestriction language extension.

Implementing these four changes results in the following complete file:

{-# LANGUAGE NoMonomorphismRestriction, TypeFamilies #-}
import Diagrams.TwoD
import Data.Colour.Names
import Diagrams.Attributes
import Diagrams.Core
import Diagrams.Util
import Data.Semigroup
import Diagrams.TrailLike

emptyBlock = rect 3 1 # fc white # lc white
(||||), (====) :: (Juxtaposable a, V a ~ R2, Semigroup a, TrailLike a, HasStyle a, Transformable a) => a -> a -> a
x |||| y = x ||| emptyBlock ||| y
x ==== y = x === emptyBlock === y

However, this still leaves a few things to be desired:

  • If you use this to separate diagrams that are over top something that isn't white, the spacer will still be drawn, potentially eclipsing some part of the underlying diagram.
  • Rectangles extend in two dimensions, meaning that if you use this to horizontally align diagrams shorter than your spacer or vertically align diagrams narrower than your spacer, the resulting composite will have an incorrect envelope.

You can fix both of these things using strutX and strutY instead of emptyBlock. This will also be more general: you won't need to include the odd-seeming stylable/transformable constraints. An example of this approach would be:

x ==== y = x === strutY 1 === y
x |||| y = x ||| strutX 3 ||| y