2

It turns out that the annularWedge function of the diagrams package cannot take a radius of 0 for the inner radius. You must use wedge instead.

To me, an inner radius of 0 is just the degenerate case of annularWedge and should behave like wedge, so I tried to combine them:

mywedge r2 r1 d a
  | r1 == 0   = wedge r2 d a
  | otherwise = annularWedge r2 r1 d a

Of course, it doesn't work, and I cannot figure out what the error means:

Non type-variable argument in the constraint: RealFloat (N t)
(Use FlexibleContexts to permit this)
When checking that ‘mywedge’ has the inferred type
  mywedge :: forall t.
             (RealFloat (N t), TrailLike t, V t ~ V2) =>
             N t -> N t -> Direction V2 (N t) -> Angle (N t) -> t

In fact, it turns out that annularWedge and wedge have different constraints, which surprised me:

annularWedge :: (TrailLike t, V t ~ V2, N t ~ n, RealFloat n) => n -> n -> Direction V2 n -> Angle n -> t

wedge :: (InSpace V2 n t, OrderedField n, TrailLike t) => n -> Direction V2 n -> Angle n -> t

So how do I combine these two functions into a sane one that accepts an inner radius of 0 and does the right thing?

duplode
  • 33,731
  • 7
  • 79
  • 150
rityzmon
  • 1,945
  • 16
  • 26

2 Answers2

3

The solution is simple. As ErikR said, add {-# LANGUAGE FlexibleContexts, TypeFamilies #-} to the top of your file. To be clear, these are both safe extensions to add (some extensions, like overlapping instances or undecidable instances should make you pause before you enable them, but these extensions are mostly safe)

Although it isn't obvious, the wedge signature's constraints are strictly weaker than the annularWedges ones.

If you are curious as to why the signatures look so different, read on...

For starters, the constraints, once you track them down, aren't all that different. Let's start with wedge.

(InSpace V2 n t, OrderedField n, TrailLike t)

Looking at the definition of InSpace, you see that it has no functions, it's essentially like a synonym: (V a ~ v, N a ~ n, Additive v, Num n) => InSpace v n a. Then, we can expand InSpace V2 n t to (V t ~ V2, N t ~ n, Additive V2, Num n). Similarly, OrderedField turns out to just be shorthand for (Floating n, Ord n). Now, the constraint for wedge looks like

(TrailLike t, V t ~ V2, N t ~ n, Additive V2, Num n, Floating n, Ord n)

However, it turns out that we can even drop the Additive V2 constraint, since that instance is defined where Additive is defined in Linear.Vector and Num n is redundant, given that Num n => Fractional n => Floating n. That leaves us with the much simpler constraint for wedge

(TrailLike t, V t ~ V2, N t ~ n, Floating n, Ord n)

Which looks a whole lot like the constraint for annularWedge. In fact, the only difference is that wedge constrains (Floating n, Ord n) compared to annularWedge constraining RealFloat n. At this point, the constraints are similar enough to make it worth looking at the code for wedge. It turns out that wedge makes use of _theta, defined in HasTheta and the corresponding V2 instance uses an arctangent function (which, you get after chasing through a couple more dependencies).

Community
  • 1
  • 1
Alec
  • 31,829
  • 7
  • 67
  • 114
2

Just follow the suggestions that GHC gives you:

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}

import Diagrams
import Diagrams.TwoD.Arc

mywedge r2 r1 d a
  | r1 == 0   = wedge r2 d a
  | otherwise = annularWedge r2 r1 d a

In general, though, I prefer to use a <= comparison when dealing with floating point numbers, i.e. something like:

  | r1 <= 1e-10 = wedge r2 d 1
ErikR
  • 51,541
  • 9
  • 73
  • 124