-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Turn R2 into D2 (Generalize R2 to any numeric type) #50
Comments
if no one's attacking this, i'll do it |
I just did first parts of the reification of R2 to D2 in my fork. I am at the point where I have to (or should) touch P2 and T2 and generalize them to (P2 a) and (T2 a) so they can work together with (D2 a). So there is the following design decision to be made: Shall I introduce something like (PD2 a) and (TD2 a) and set P2 = PD2 Double and T2 = TD2 Double to keep old code using those types working or shall I just break things there? |
In an ideal world I think I agree that writing Generally speaking we have generally prioritized doing things the right way over backwards compatibility. So I think I would be in favor of doing something like
Anyone else have thoughts on this? |
+1 on generalizing so we can have nice things like automatic differentiation. ;) That said, I don't exactly have a huge installed diagrams code-base, so this is a painless call for me. =) |
Oh, sure, we're definitely going to generalize. At this point we're just bikeshedding about names. ;-) |
Talking about bikeshedding about names: You also suggested to generalize
If we break code anyway, I am for the general naming scheme, because it would be easier to memorize. Although we should maybe use |
Hmm, I see jbracker's email, but not the comment above. Anyway, there were issues with generalizing unitX / unitY, related to being able to enumerate the axis of a vectorspace. I'm not currently on a computer with ghci, but it seems like the following might be a reasonable definition: unitX :: (Decomposition v ~ (a :& a), Num a) => v
unitX = 1 & 0 This is good because when generalizing stuff inside "Diagrams.TwoD", it really ought to be for two dimensional stuff. Regarding naming, I'm in support of leaving |
It might be a good general strategy for this conversion to just delete the types, and see what the most polymorphic type for the current definition is. I suspect something like the above would be inferred. One thing to consider for the type of |
I deleted the post, because minutes later I realised that there is a pretty simple solution: unitX :: (Num a) => D2 a
unitX = 1 & 0 |
Oh, you're right, |
Also, @jbracker , go ahead and open a pull request from your repo -- I won't merge it until you are done, but it's a nice way to let others see and give feedback on your work in progress. As you push more commits they will automatically be incorporated into the pull request. |
Currently trying to generalize the code in Diagrams/TwoD/Transform.hs (see my fork). But I am kind of stuck. I get a bunch of errors that tell me that it can not deduce Either the problem occures because the different newtype CircleFrac a = CircleFrac { getCircleFrac :: a } But with that the class (Num a) => Angle m a where
toCircleFrac :: m a -> CircleFrac a
fromCircleFrac :: CircleFrac a -> m a
instance (Num a) => Angle CircleFrac a where
toCircleFrac = id
fromCircleFrac = id
instance (Floating a) => Angle Rad a where
toCircleFrac = CircleFrac . (/tau) . getRad
fromCircleFrac = Rad . (*tau) . getCircleFrac
instance (Fractional a) => Angle Deg a where
toCircleFrac = CircleFrac . (/360) . getDeg
fromCircleFrac = Deg . (*360) . getCircleFrac And the definition of fullCircle :: (Angle m a) => a
fullCircle = fromCircleFrac 1 -- ERROR: Could not deduce (a ~ m2 a1)
convertAngle :: (Angle m1 a, Angle m2 b) => a -> b
convertAngle = fromCircleFrac . toCircleFrac -- ERROR: Could not deduce (b ~ m0 a0)
-- ERROR: Could not deduce (a ~ m1 a0) My other suspect is the |
Ok, my mistake: fullCircle :: Angle m a => m a
fullCircle = fromCircleFrac 1
convertAngle :: (Angle ma a, Angle mb a) => ma a -> mb a
convertAngle = fromCircleFrac . toCircleFrac Still can't think of another way to represent angles pleasantly. With this definition I can rewrite the signatures like this: rotateAbout :: ( Floating a, HasBasis a, HasTrie (Basis a), a ~ Scalar a, Transformable t, V t ~ D2 a, Angle m a) => P2 a -> m a -> t -> t This requires to use the coordinate type as type to represent a angle. I am not sure if that is an issue (because |
Sorry I haven't had a chance to look at this in a while. I hope to have a bit more time now. Yikes, this Sorry this is turning out to be quite a bit more fiddly than I thought. |
A good example for why I had to generalize it is the In its original form it looked like this: rotation :: Angle a => a -> T2
rotation ang = fromLinear r (linv r)
where
r = rot theta <-> rot (-theta)
Rad theta = convertAngle ang
rot th (coords -> x :& y) = (cos th * x - sin th * y) & (sin th * x + cos th * y) Now it looks like this: rotation :: ( AdditiveGroup a
, Floating a
, HasBasis a
, HasTrie (Basis a)
, a ~ Scalar a
, Angle m a
) => m a -> T2 a
rotation ang = fromLinear r (linv r)
where
r = rot theta <-> rot (-theta)
Rad theta = convertAngle ang
rot th (coords -> x :& y) = (cos th * x - sin th * y) & (sin th * x + cos th * y) In the initial form the coords transformed used newtype CircleFrac a = CircleFrac { getCircleFrac :: a }
deriving (Read, Show, Eq, Ord, Enum, Floating, Fractional, Num, Real, RealFloat, RealFrac) But the type class was a tricky. Originally it looked like this: class Num a => Angle a where
toCircleFrac :: a -> CircleFrac
fromCircleFrac :: CircleFrac -> a But as the angle representations are now parametrized with a type you get a problem. My first approach was: class Num a => Angle a where
toCircleFrac :: a -> CircleFrac a
fromCircleFrac :: CircleFrac a -> a Which obviously will not type check either way (instance class (Num a, Num (m a)) => Angle m a where
toCircleFrac :: m a -> CircleFrac a
fromCircleFrac :: CircleFrac a -> m a |
What is currently in my fork should have all types generalized and compile. I will tag it. I am currently working on generalizing the if-then-else using the Data.Boolean package to enable the deep embedding. There is an issue arising from this. For Data.Boolean to work you have to associate a boolean type to each type you want to use in an if-then-else. I have thought about a couple of solutions for this problem:
So I will generalize further using the second approach. Also as there is no protest I will rename |
Hi @jbracker , it was great to meet you and hack on this a bit yesterday! Thanks for your hard work on this, it seems there is still a ways to go but I am confident we can make it all work. The first solution sounds the best to me, and I do not understand why it introduces cyclic dependencies. I would have thought that
Is there something I'm misunderstanding? |
Hi @byorgey, I and enjoyed that micro-hack too.
|
Hmm, I don't understand your comment about "a default back end will not work anymore"... can you explain further what you mean? There is no "default backend"; to use any backend the user must import it anyway. And if they only want to define diagrams and not render them, then it seems it should not matter whether |
In all the examples I tried and looked at it always seemed as if there was one. Never mind. You are right, when just defining them, it should not make a difference. For future reference and just in case someone here is not reading the mailing list, there is another problem going on: |
Some further Problems have occured:
|
I've always thought that the partialness of One option that I think has been discussed is to add I think the most sensible solution is to add "tzero", or something like that, it to the |
I don't ever recall discussing adding Off the top of my head, though, I really like your idea of adding |
Any news on this? I was hoping to use diagrams-lib a starting point for a geometry library. But having generalized number types is an absolute requirement. I briefly looked at the generalized-R2 branch and generalized-types tags. But it seems a lot of code has changed in the mean time (which also makes me think that it may be a good idea to do this type of change gradually instead of a wholeshot change). |
Unfortunately it doesn't look like this is going to happen anytime soon. @jbracker did some fantastic work, but
You're right that at this point development has diverged to the point that we would basically have to do a lot of the work over again (which I'd still be open to, if someone figured out a way to do the generalization in a clever way that didn't introduce a ton of complication). You say we ought to do this gradually instead of wholeshot, but I really don't see how that would be possible. The |
Wr.t. to being invasive and the wholeshot comment: If I look at the generalized-R2 branch than it seems that this actually tries to do three things:
I'm not sure what the reasons for 3) are, as I was not familiar with memotries until reading Ralf's paper yesterday. However, it seems to me that this is something that can be considered separately. As for the other two: I think it would already be very usefull to have part 1) in place, since: (a) this would allow other people/packages to already use the impressive amount of transformations on geometric objects, (b) it can be implemented without affecting too much of the code, (c) that part of the code does not become that much more complicated, and (d) it is a prequisite for actually getting to 2), (also in the sense that for any new code people will build on top of what is already there, so delaying making things more general will only increase the amount of work that needs to be done when actually doing it). When part 1) is in place the parts of the library that produce drawings can be upgraded to use generic types incrementally. As I am not a coach or boss telling other people how to do their job, I did some work myself as well ;). I redid a part of the work required for 1), which can be found in [1]. All commits up to yesterday (Sept 5, 2013) step by step generalize the types and transformations on types. I'm not entirely happy about the stuff with Angles and Turns, since I think I made that too complicated. The commits of today (Sept 6, 2013) are part of 2): generalizing the code that actually produces parts of the diagrams. I think this is also were most of the complexity was/is when generalizing things. So far I looked only at the Paths module, but I think that the result is also not too horrible. I used ConstraintKinds to create type aliasses for some collection of constraints that often occur simultaneously (Essensially the diagrams-core library already uses something similar in e.g. the [1] https://github.com/noinia/diagrams-lib/tree/gentypes |
I'd like to revisit this at some point. Is there a list of features that might benefit from the more general types? I'm aware of:
I also have a (possibly controversial) idea for handling |
I don't know of any others, but that's a pretty good list. As for what we would use AD for, think along the lines of e.g. auto-fitting shapes together (by minimizing envelope distance or something like that). I haven't yet taken a careful look at @noinia 's changes (linked above). |
This happened with the linear port. |
Imported from diagrams/diagrams-core#20, since it actually affects the diagrams-lib repo. See the discussion there for more information. The basic idea is to create a new type
and to define
and to then generalize type signatures mentioning
R2
to be generic over anyD2 a
wherea
is a type with the required properties. For example, we would generalizeto
or something similar.
This process should be mostly mechanical, except that some care will be needed in coming up with the right (minimal) constraints. For example, I don't actually know whether
Floating
is required for(===)
, or if something likeFractional
(or something else entirely) will do.This will pave the way for doing various fun things with 2D diagrams over generalized numeric types, such as
Of course, the same should also be done for 3D diagrams but that can wait a bit.
The text was updated successfully, but these errors were encountered: