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
Support dependent function types #4751
Comments
Imported From: https://issues.scala-lang.org/browse/SI-4751?orig=1 |
Rich Oliver (rich oliver) said: |
Denis Petrov (denis) said: |
@retronym said: scala> trait DepFunction1[-A <: AnyRef, +Return[_]] { def apply(a: A) : Return[a.type] }
warning: there were 1 feature warnings; re-run with -feature for details
defined trait DepFunction1
scala> trait NewFunction1[-A <: AnyRef, +R] extends DepFunction1[A, ({type l[_] = R})#l]
defined trait NewFunction1
scala> object stringId extends DepFunction1[String, ({type Id[x] = x})#Id] { def apply(s: String): s.type = s }
defined object stringId Where |
@Blaisorblade said (edited on Jan 18, 2013 4:44:43 PM UTC): It takes a small variant for DepFunction1 to express the type of: def f(a: T1)(b: a.MemberT2): b.MemberT3 I'll parametrize over def f[T1 <: {type MemberT2 <: {type MemberT3}}](a: T1)(b: a.MemberT2): b.MemberT3 = ??? But I assume a concrete type with the right members would also work. I was just too bored to make things that simple :-). The problem is that for the above case, Return cannot be defined over all types, only over subtypes of T1 with the right type member. So with your DepFunction1, I get this kind error: scala> def g[T1 <: {type MemberT2 <: {type MemberT3}}]: DepFunction1[T1, ({type Ret[x <: T1] = DepFunction1[x#MemberT2, ({type Ret[y <: x#MemberT2] = y#MemberT3})#Ret]})#Ret] = ???
<console>:8: error: kinds of the type arguments (T1,[x <: T1]DepFunction1[x#MemberT2,[y <: x#MemberT2]y#MemberT3]) do not conform to the expected kinds of the type parameters (type A,type Return) in trait DepFunction1.
[x <: T1]DepFunction1[x#MemberT2,[y <: x#MemberT2]y#MemberT3]'s type parameters do not match type Return's expected parameters:
type x's bounds <: T1 are stricter than type _'s declared bounds >: Nothing <: Any
def g[T1 <: {type MemberT2 <: {type MemberT3}}]: DepFunction1[T1, ({type Ret[x <: T1] = DepFunction1[x#MemberT2, ({type Ret[y <: x#MemberT2] = y#MemberT3})#Ret]})#Ret] = ???
^ |
@Blaisorblade said: //Variant: Note that Return must only accept subtypes of A
trait DepFunction1[-A <: AnyRef, +Return[_ <: A]] { def apply(a: A) : Return[a.type] }
//Your examples still work - the kind needn't match:
object stringId extends DepFunction1[String, ({type Id[x] = x})#Id] { def apply(s: String): s.type = s }
trait Function1[-A <: AnyRef, +R] extends DepFunction1[A, ({type l[_] = R})#l]
//Now we can write:
def g[T1 <: {type MemberT2 <: {type MemberT3}}]: DepFunction1[T1, ({type Ret[x <: T1] = DepFunction1[x#MemberT2, ({type Ret[y <: x#MemberT2] = y#MemberT3})#Ret]})#Ret] = ??? That worked well! But we didn't yet eta-expand f, only represented the interface for that. There, I get an error I don't get (no pun intended): scala> def g[T1 <: {type MemberT2 <: {type MemberT3}}]: DepFunction1[T1, ({type Ret[x <: T1] = DepFunction1[x#MemberT2, ({type Ret[y <: x#MemberT2] = y#MemberT3})#Ret]})#Ret] =
| new DepFunction1[T1, ({type Ret[x <: T1] = DepFunction1[x#MemberT2, ({type Ret[y <: x#MemberT2] = y#MemberT3})#Ret]})#Ret] {
| def apply(a: T1) = new DepFunction1[a.MemberT2, ({type Ret[y <: a.MemberT2] = y#MemberT3})#Ret] {
| def apply(b: a.MemberT2): b.MemberT3 = f(a)(b) //": b.MemberT3" is required, else we get more errors.
| }
| }
<console>:10: error: type mismatch;
found : DepFunction1(in object $iw)[a.MemberT2,[y <: a.MemberT2]y#MemberT3]
required: DepFunction1(in object $iw)[a.MemberT2,[y <: a.MemberT2]y#MemberT3]
def apply(a: T1) = new DepFunction1[a.MemberT2, ({type Ret[y <: a.MemberT2] = y#MemberT3})#Ret] {
^ |
@retronym said: |
@Blaisorblade said: Minimized: scala> def g[T1 <: {type MemberT2}]: DepFunction1[T1, ({type Ret[x <: T1] = x#MemberT2})#Ret] =
| new DepFunction1[T1, ({type Ret[x <: T1] = x#MemberT2})#Ret] {
| def apply(a: T1) = f2(a)
| }
<console>:11: error: type mismatch;
found : a.MemberT2
required: a.MemberT2
def apply(a: T1) = f2(a)
^ Workarounded: scala> def g[T1 <: {type MemberT2}]: DepFunction1[T1, ({type Ret[x <: T1] = x#MemberT2})#Ret] =
| new DepFunction1[T1, ({type Ret[x <: T1] = x#MemberT2})#Ret] {
| def apply(a: T1): a.MemberT2 = f2(a)
| }
g: [T1 <: AnyRef{type MemberT2}]=> DepFunction1[T1,[x <: T1]x#MemberT2] Note that I just added an explicit type annotation. scala> def g[T1 <: {type MemberT2 <: {type MemberT3}}]: DepFunction1[T1, ({type Ret[x <: T1] = DepFunction1[x#MemberT2, ({type Ret[y <: x#MemberT2] = y#MemberT3})#Ret]})#Ret] =
| new DepFunction1[T1, ({type Ret[x <: T1] = DepFunction1[x#MemberT2, ({type Ret[y <: x#MemberT2] = y#MemberT3})#Ret]})#Ret] {
| def apply(a: T1): DepFunction1[a.MemberT2, ({type Ret[y <: a.MemberT2] = y#MemberT3})#Ret] =
| new DepFunction1[a.MemberT2, ({type Ret[y <: a.MemberT2] = y#MemberT3})#Ret] {
| def apply(b: a.MemberT2): b.MemberT3 = f(a)(b)
| }
| }
g: [T1 <: AnyRef{type MemberT2 <: AnyRef{type MemberT3}}]=> DepFunction1[T1,[x <: T1]DepFunction1[x#MemberT2,[y <: x#MemberT2]y#MemberT3]] |
@Blaisorblade said:
○ : {A : Set} {B : A -> Set} {C : (x : A) -> B x -> Set} → A : Set means that A is a type parameter, and arguments between {} are just Agda's implicits (which are deduced by unification) - some of that is what I'd like in Scala as well: |
@milessabin said (edited on May 21, 2013 5:35:52 PM UTC): scala> trait Dep { type T }
defined trait Dep
scala> implicit def m[T0](d : Dep { type T = T0 })(implicit t : T0) = t
m: [T0](d: Dep{type T = T0})(implicit t: T0)T0
scala> def n[T0](d : Dep { type T = T0 })(implicit f : Dep { type T = T0} => T0) = f(d)
n: [T0](d: Dep{type T = T0})(implicit f: Dep{type T = T0} => T0)T0
scala> val dep = new Dep { type T = String }
dep: Dep{type T = String} = $anon$1@4b6393b3
scala> implicit val s : String = "foo"
s: String = foo
scala> val r = n(dep)
r: String = foo This is simply trading the dependent type |
@Blaisorblade said:
|
@retronym said: Details
Type:Bug Bug
Status:CLOSED
Priority:Major Major
Resolution: Duplicate
Affects Version/s:
Scala 2.10.3, Scala 2.10.4
Fix Version/s: None
Component/s:
Type Checker, Type Inference
Labels:
existential type-inference
Description
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45).
Type in expressions to have them evaluated.
Type :help for more information.
scala> class C { trait T }
defined class C
scala> val c = new C
c: C = C@1130ed80
scala> val t1: c.T = (new { def apply(c: C) = new c.T {} })(c)
t1: c.T = $anon$2$$anon$1@23309add
scala> val t2: c.T = ({c: C => new c.T {} })(c)
<console>:9: error: type mismatch;
found : $anon where type $anon <: c.T
required: c.T
val t2: c.T = ({c: C => new c.T {} })(c)
^
scala> val f1: { def apply(c: C): c.T } = {c: C => new c.T {} }
<console>:9: error: type mismatch;
found : C => $anon forSome { type $anon <: c.T; val c: C }
required: AnyRef{def apply(c: C): c.T}
val f1: { def apply(c: C): c.T } = {c: C => new c.T {} }
^
The function literal is inferred to existential type
C => $anon forSome { type $anon <: c.T; val c: C }
, which looks complex, but it is no more special than simple C => C#T.
Could you infer the function literal to
(C => C#T) with { def apply(c: C): c.T } |
@Blaisorblade said: scala> val t2: c.T = ({c: C => new c.T {} })(c)
<console>:9: error: type mismatch;
found : $anon where type $anon <: c.T
required: c.T
val t2: c.T = ({c: C => new c.T {} })(c) |
Also improve test coverage by: - portraying the different possible translations of the intMonoid example - demonstrating issues with the scalac compiler's type inference mechanism
out of scope for Scala 2 (if someone would like to comment with links to information about where Scala 3 stands on this, please do) |
Sample REPL session with -Ydependent-method-types. The following with a dependent argument type fails,
whereas with a type projection (ie. changing d.T to Dep#T) we succeed,
Having both these constructs supported equally would be highly desirable for the usability of dependent methods in Scala.
A more complete example which illustrates the usefulness of dependent function types,
Here we would like m to be considered as a candidate for the implicit f in n. It isn't because eta expansion would generate a dependent function type corresponding to m.
The text was updated successfully, but these errors were encountered: