GithubHelp home page GithubHelp logo

Comments (14)

RyanGlScott avatar RyanGlScott commented on May 3, 2024 1

FWIW, I asked some of the folks who work on the ghc-typelits-natnormalise typechecker plugin if it would be possible to give GHC the power to typecheck a function like replicateSucc above. They didn't answer with a definitive "no", but it definitely seems like it's trickier than one would expect: clash-lang/ghc-typelits-natnormalise#13

from eliminators.

RyanGlScott avatar RyanGlScott commented on May 3, 2024

I don't think there's anything conceptually wrong with your code. I think the real culprit is that you're trying to use GHC.TypeLits.Nat to prove this. The reason being that Nat isn't an inductive data type, and as such, GHC can't figure out many things that would otherwise be simple. (I'd go as far to call Nat an ugly hack that GHC only added so you could write numeric literals at the type level.)

Admittedly, I did advertise elimNat as a cute trick to treat Nat inductively. And for certain things, this claim holds water, but I don't think you're going to be able to get away with it here.

If you look at the definition of replicate in singletons, perhaps you'll get a better idea of why GHC is having trouble proving your claim:

-- Functions working on infinite lists don't promote because they create
-- infinite types. replicate also uses integers, but luckily it can be rewritten
-- ...
  replicate               :: Nat -> a -> [a]
  replicate n x           = if n == 0 then [] else x : replicate (n-1) x

As a result, the type-level Replicate n gets stuck on any n that isn't statically known to be 0, 1, or some other concrete literal. Sad news.

My advice: don't give yourself any more Nat headaches if you can avoid it. Just switch to a proper inductive natural number definition:

$(singletons
  [d| data Peano = Zero | Succ Peano

      replicate :: Peano -> a -> [a]
      replicate Zero     _ = []
      replicate (Succ n) x = x : replicate n x
    |])

And your proof will go through a lot more smoothly.

from eliminators.

LeanderK avatar LeanderK commented on May 3, 2024

Hmm. Is there a way to use the isomorphism between the representations to prove this? I can't use Peano-Types directly, I rely on fast integers for other operations. I still don't understand this fully. I would assume that adding (ToDomain (Replicate (z :+ 1) (a -> b)) ~ (a : ToDomain (Replicate (z) (a -> b)))) to step would solve the problem for Step (and just introduce another problem later).
Also, I am still not sure how to actually use the proof once it's working.
EDIT: Can i unsafecoerce something like this? 😀 I know this kinda defeats the purpose, but I am still in the experimenting-phase and the code I want to write doesn't compile yet.

from eliminators.

RyanGlScott avatar RyanGlScott commented on May 3, 2024

Hmm. Is there a way to use the isomorphism between the representations to prove this?

I'm not sure which isomorphism you're referring to here. (You can hand-wave and say "of course Nat and Peano are obviously isomorphic, but we need something to convince GHC of this fact. This proves to be trickier than you might expect.)

I rely on fast integers for other operations.

OK. This isn't an all-or-nothing scenario—it's possible to use both Nats and Peanos in the same program, using these conversion functions:

$(singletons
  [d| data Peano = Zero | Succ Peano

      fromNat :: Nat -> Peano
      fromNat n | n  < 0 = error "fromNat n | n < 0"
                | n == 0 = Zero
                | n  > 0 = Succ (fromNat (n-1))

      toNat :: Peano -> Nat
      toNat Zero = 0
      toNat (Succ n) = 1 + toNat n
    |])

(Perhaps this is the "isomorphism" you were referring to earlier?)

Also, I am still not sure how to actually use the proof once it's working.

I'm not sure what you mean. doaminMatches is a function like any other—just give it the arguments it expects, and it'll do its thing.

Can i unsafecoerce something like this? 😀 I know this kinda defeats the purpose, but I am still in the experimenting-phase and the code I want to write doesn't compile yet.

Sure, it's quite common to see Nat-heavy code assume a bunch of axioms about Nat using the following utility function:

axiom :: a :~: b
axiom = unsafeCoerce Refl

myHardToProveFact :: Foo :~: Bar
myHardToProveFact = axiom

Of course, this assumes that you, the smart programmer, can show that this axiom holds, so use this with caution.

from eliminators.

LeanderK avatar LeanderK commented on May 3, 2024

I'm not sure which isomorphism you're referring to here. (You can hand-wave and say "of course Nat and Peano are obviously isomorphic, but we need something to convince GHC of this fact. This proves to be trickier than you might expect.)

I should have formulated this in a more clear way, but this is what I thought of. I am a bit new to all this, but it seemed like a pretty straightforward thing in my mind.

I'm not sure what you mean. doaminMatches is a function like any other—just give it the arguments it expects, and it'll do its thing.

Well, I have a function where ToDomain (Replicate n (a -> b)) ~ Replicate n a could not get deduced by the compiler. If I understood it right I am building a proof, but I am not sure what the relationship between ~ and ':~:` actually is, do I have to singletonize the proof again? I have to use it somehow.

I actually encountered the Nat axioms today, so I could just write them all down right now 😉 (well, they are not hard either) I can't image that this would solve my problem though.
EDIT: I may have misunderstood your example. Is axiom literally meant to be used to unsafeCoerce things (which is when I think about it pretty much what in this context an axiom does) and not represent the commonly used axioms of things (like Nats)?

from eliminators.

RyanGlScott avatar RyanGlScott commented on May 3, 2024

Let's motivate a use of axiom by using it to complete your proof. You're getting stuck because GHC can't conclude the following fact:

replicateSucc :: forall k a. Replicate (k :+ 1) a :~: (a : Replicate k a)

Again, this would be dead simple if k were an inhabitant of actual inductive data type. But Nat is not such a type, and the definition of Replicate makes it so that it's not particularly amenable to inductive reasoning. So one way you could admit this fact it to use axiom:

replicateSucc :: forall k a. Replicate (k :+ 1) a :~: (a : Replicate k a)
replicateSucc = axiom

Now the step case of domainMatches simply becomes:

                step _ = case replicateSucc @k @(a -> b) of
                           Refl -> case replicateSucc @k @a of
                             Refl -> cong @_ @_ @((:$$) a)

Perhaps there's a way to define replicateSucc without having to resort to axiom. But if there is, I'm nowhere near smart enough to figure it out ;)

from eliminators.

LeanderK avatar LeanderK commented on May 3, 2024

Thank you.
Short question (I know it's offtopic, feel free to give me feedback to not misuse these issues).
What is the best way to convince the type-checker of implications, like OnlyFunctions (a: as) => a ~ (x -> y)?

from eliminators.

RyanGlScott avatar RyanGlScott commented on May 3, 2024

What is the best way to convince the type-checker of implications, like OnlyFunctions (a: as) => a ~ (x -> y)?

When you say "convince", do you mean in a global sense? As in convincing GHC to always use OnlyFunctions (a: as) => a ~ (x -> y) when solving constraints? If so, that isn't currently possible. (Short of a GHC typechecker plugin like ghc-typelits-natnormalise, but I wouldn't recommend implementing it this way unless you're a glutton for pain ;) ).

If you mean in a local sense (as in convincing GHC to use OnlyFunctions (a: as) => a ~ (x -> y) in some particular scope), then the most straightforward way to conjure up an axiom cleverNameHere :: OnlyFunctions (a: as) => a :~: (x -> y) and pattern-match on it, similarly to what is done in #3 (comment).

from eliminators.

LeanderK avatar LeanderK commented on May 3, 2024

Well, that's pretty straightforward, I could have thought of that 🙂
A local scope is fine.
This raises of course more questions (Why would a type-checker plugin be so bad? Isn't this use-case exactly where they would shine? I imagine doing it the proper way in GHC to take a long time before it arrives in the users code, also I expect working on GHC to be difficult...)
Thank you!

from eliminators.

LeanderK avatar LeanderK commented on May 3, 2024

I just read your blogpost about quantified contexts and wondered whether this could help here:
Is something like this possible?

forall k. KnownNat k => (k + 1 /~ 0)

where
/~ is not equal (is something like this existing?)

from eliminators.

RyanGlScott avatar RyanGlScott commented on May 3, 2024

Nope—GHC has no notion of "inequality constraints".

from eliminators.

LeanderK avatar LeanderK commented on May 3, 2024

that's unfortunate...I bet there's a reason, it seems so fundamental

from eliminators.

RyanGlScott avatar RyanGlScott commented on May 3, 2024

The most straightforward answer for why there's no inequality constraints is because GHC's type system is based off of intuitionistic logic, where the law of excluded middle (i.e., every proposition is either true or false) does not hold. In intuitionistic logic, every proposition is either provable, or assuming it would lead to a contradiction. In other words:

Either a (a -> Void)

This is a trick that I use a lot in the eliminators library—e.g., here. But before you ask, this trick won't work for GHC.TypeNats.Nat, since GHC doesn't have the ability to tell that n and n + 1 are apart, since (+) is not inductively defined.

from eliminators.

LeanderK avatar LeanderK commented on May 3, 2024

This is really confusing! 😀
I have take some time and read what that really means...

from eliminators.

Related Issues (7)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.