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
Auto Implicit Return Values #1003
Comments
Variables are already available to For instance: an : (a : Type) -> a => a
an a @{p} = p
withAuto : Int
withAuto = let p : Int = 3 in an Int
|
I'm confused. Can you explain how to write |
Yes! Turns out you don't have to! Since your goal seem to be to make values available for
There is an additionnal level of cleverness in that example and that is
is the same as
so that is easier to type. Additionally, if you want an easier way to write your implicits you can create a custom operator that looks like an arrow, let' say
|
@andrevidela You still haven't answered my question. Can you explain how to write |
@michaelmesser simply bind the proof to a variable:
will make the proof |
@michaelmesser please also note, that a data type like your |
@andrevidela I spoke with @Russoul about this on Slack and he described this as "sugar for automatic wrapping and unwrapping of dependent pairs". Does that help you understand what I'm asking for? Also |
@stefan-hoeck I'm aware |
Would this work for you? Edit: with comments prefix 0 #
record Implicit {a : Type} (p : a -> Type) where
constructor (#)
value : a
{auto 0 proof : p value}
data Even : Nat -> Type where
Zero : Even Z
SSuc : Even n -> Even (S (S n))
anEven : Implicit Even
anEven = # 2
PPred : Even (S (S n)) -> Even n
PPred (SSuc p) = p
it : a => a
it @{p} = p
half : (n : Nat) -> {auto 0 _ : Even n} -> Nat
half Z = Z
half (S (S n)) = let 0 p = PPred it in S (half n)
anHalfEven : Nat
anHalfEven = let (# n) = anEven in half n |
This is probably what michaelmesser wants, applied to your example, gallais:
Wrapping of values |
Technically speaking, for this specific example, it could be possible to implement a custom |
Russoul's comment correctly describes what I'm looking for. namespace Context
export
pure : (fst : type) -> {auto 0 p : pred fst} -> Subset type pred
pure x {p} = Element x p
export
(>>=) : Subset type pred -> ((x : type) -> {auto 0 p : pred x} -> Subset type' pred') -> Subset type' pred'
(>>=) (Element x _) f = f x
None : a -> Type
None _ = () data Even : Nat -> Type where
Zero : Even Z
SSuc : Even n -> Even (S (S n))
%hint
PPred : Even (S (S n)) -> Even n
PPred (SSuc p) = p
anEven : Subset Nat Even
anEven = pure 2
half : (n : Nat) -> {auto 0 _ : Even n} -> Nat
half Z = Z
half (S (S n)) = S (half n)
anHalfEven : Subset Nat None
anHalfEven = do
n <- anEven
pure $ half n However, I want to be able to use data CT : (m : Type -> Type) -> (a : Type) -> (a -> Type) -> Type where
MkCT : m (Subset a ps) -> CT m a ps
namespace CT
export
pure : Applicative m => (fst : type) -> {auto 0 p : pred fst} -> CT m type pred
pure x = MkCT (pure (pure x))
export
(>>=) : Monad m => CT m type pred -> ((fst : type) -> {auto 0 p : pred fst} -> CT m b qs) -> CT m b qs
(>>=) (MkCT io) f = MkCT $ do
Element x _ <- io
let MkCT z = f x
z But I couldn't figure out how to write a |
Implicit conversions are evil. Hence why I believe this belongs in library land rather than in the compiler
Right. The direct-style solution should not have the same problems. |
How is an auto implicit return value any more evil than an auto implicit argument? Idris already does automatic |
Implicit arguments are solved by unification which is a principled In the example I cooked you could return
Implicit coercions in general make type-checking hell: when you have types |
I meant to say auto implicit arguments (which I do not believe are solved by unification). |
@michaelmesser The current proposal about removing Implicit conversions in the upcoming Scala 3 sums it up pretty well regardless of implementation details https://contributors.scala-lang.org/t/can-we-wean-scala-off-implicit-conversions/4388 Auto implicits do not generate any confusions about existing written programs (at least not more than dependent types and type classes). |
Most usages of auto implicits are ill-advised. They're a very dangerous |
gallais you can avoid using |
You're still doing arbitrary proof search instead of highlighting the fact |
I don't see why that is a problem. I don't want to think about the proofs. I want Idris to figure them out. |
How does auto implicit returns generate confusion about existing written programs? Unless Also I'm pretty sure Scala has subclasses and implicit upcasting which is the feature I'm trying to emulate. |
It won't (not any time soon at least). What you describe is way closer to In turn you have to compromise and have a less powerful logic to make that |
imagine the following program:
imagine now that you import a library to do arithmetic computation and it has somewhere this function exported:
what would your program print? Now imagine you are not dealing with numbers but HTML templates, which themselves have values which are implicitly converted. You can't tell what's going on until you print the HTML output, assuming you can. If you can't it may be because of a type error on your part, OR it might because of an implicit conversion not triggering properly because something hasn't been imported/implemented/used correctly. Either way the error is going ot be impossible to report because you cannot tell when a type mismatch happens because of an implicit conversion that fired and shouldn't have, or because it's a legitimate error. Finally you have funny programs like this:
What do you think will happen? What should happen? And most importantly, how would the compiler know what is your intent? Now imagine you put this in a compile-time function, you mistakenly type |
@andrevidela I'm not talking about |
@andrevidela The key difference between my proposal and general implicit is the following.
However with my proposal |
I'm wondering why if we would slightly modify code from the Guillaume's comment above, the behaviour is different: anHalfEven : Nat
anHalfEven = let x = anEven
in half x.value gives
But anHalfEven : Nat
anHalfEven = let x : Implicit Even
x = anEven
in half x.value typechecks. Looks like when type-ascripted, By the way, @michaelmesser, isn't this closer to what you wanted originally? |
@buzden The custom anHalfEven : Subset Nat None
anHalfEven = do
x <- anEven
pure $ half x |
IMHO, as @gallais said, implicit conversions are evil. While I agree that it may be useful in some cases to automatically carry around proofs (and the mechanism is already there), I would much prefer to have some explicit annotation that we are matching on a value and a proof, maybe a short one like the |
@ShinKage Why would auto implicit return values be any more evil than auto implicit arguments? |
@michaelmesser Oh I think that also the current version of auto-implicit arguments are sort of evil. I would advise against using it in any case where the constructed value is not uniquely determined by it's dependecies (e.g. Refl, but even Elem for List can be problematic if there are duplicate in the List as you have to assume that the auto-search always choose one way of constructing it). Furthermore I think there should be a way to explicitly annotate how to search (e.g. search interface instance or find any valid value or more complex user-provided tactics). They can be abused in ways that are hardly debuggable, as the control is not yours but of the particular algorithm used for resolution, and changes in the algorithm have impact on the compilation itself, even changing the default maximum depth can lead to perfectly fine code not compiling with some strange errors that can mislead newcomers and people not invested in the implementation of auto-implicits. There have been multiples issues and question on proofs over interfaces or nested interfaces where the auto-implicits mechanism lead people to wrong assumptions (you often need to explicitly carry around the interface implementation, which is counterintuitive). |
This is what I had in mind: If the argument has a multiplicity of zero, just pick one. |
What I'm trying to say is not that you cannot find a reasonable procedure for selecting one value over the others (you can get pretty smart about it, trading compile time speed with writing code time) but that while one may be reasonable, it's not the "correct" one. The current approach, and the reasonable procedure, are not zero-cost abstractions, there may be impact depending on the procedure (unexpected error messages or unexpected results depending on which way the procedure prefers) and there is no way currently of refining the abstraction that isn't just ditching the auto-implicit passing behaviour and explicitly pass around the values. |
@ShinKage If users want custom behavior instead of auto they can use default implicit arguments with an elaborator script. |
@michaelmesser It almost works, because if the elaborator script fails then it always fails to typecheck even if you explicitly provide a valid argument to use instead of the default one. |
I am looking for a way to implicitly return a value from a function.
Say I have the function
f : X -> Y
. But instead of just returningY
, I was hoping for some way to introduce a proof about the returned value. For example, some way to introduce a proofP y
into the...
inlet y = f x in ...
. The proof would need to be available toauto
.My proposed way of achieving that is the following. I'm not certain if this is the right approach yet and would be interested in other methods to achieve the same effect.
The compiler would implicitly convert the following:
Implicit a p
to(x : a)
introducingp x
into the local scope(x : a)
toImplicit a p
ifp x
can be found byauto
Implicit a p
toImplicit a q
by combining 1 and 2Implicit
sI have no idea how easy or hard that would be to implement or even if this feature is a good idea. I assume it would be somewhat similar to
Delay
andForce
.The use case I have in mind is emulating weird behaviors of TypeScript such as implicit upcasting. But I imagine there are other use cases as well.
This can be somewhat emulated with a weird version of
>>=
but it is less than ideal.It probably could also be emulated with implicit conversions from Idris 1.
Another use of this feature would be improvements to implicit arguments.
is cleaner than
The text was updated successfully, but these errors were encountered: