Enforcing that the function returns something (other than Unit)

293 views
Skip to first unread message

Oleg Zhurakousky

unread,
Oct 13, 2011, 10:37:28 PM10/13/11
to scala-l...@googlegroups.com
So in Java i can quickly determine if a method returns something or not by simply looking at its signature.
In Scala we have Unit which is often mistaken for a Void since it is used in places equivalent to Java void. Anyway, here is my dilemma. For example; if I want to provide a function signature that ensures that the function returns Boolean I can do this:

def foo(function: String => Boolean)

now bar.foo{m => 4 < 3} - will work fine but bar.foo{m => 4} will not. Perfect.

But how do I structure the signature of the function to make sure that the following is legal bar.foo{m => m} or bar.foo{m => m.toString()} etc., but NOT bar.foo{m => println("4")}

In other words I want to enforce that the provided function code returns something (at least from its structure) - non-Unit

Cheers

Oleg

Daniel Sobral

unread,
Oct 13, 2011, 10:59:52 PM10/13/11
to scala-l...@googlegroups.com

That's something I never saw before. I'm a bit dubious if wanting a
generic T that is necessarily non-Unit is really useful -- there are
all sorts of places where returning Unit makes perfect sense.

Anyway, this problem is akin to the problem of not accepting Nothing,
though easier, since Unit isn't a subtype of anything but AnyVal and
Any. The way around that is usually by defining a type class such with
the acceptable types, but here you have a *non*-accepted type. The
solutions I saw were a much convoluted one by Miles Sabin, involving
representing types as logic clauses, and formulating the proper
axioms, and a simpler version where the type class is contra-variant,
and both an Any and a Nothing (or, in this case, a Unit) implicit is
made available, so that the case where both types are possible gives
an ambiguity error -- not exactly an informative message.

--
Daniel C. Sobral

I travel to the future all the time.

Vlad Patryshev

unread,
Oct 14, 2011, 12:32:24 AM10/14/11
to scala-l...@googlegroups.com
The question is, what's so special about Unit.

The same question can be asked about any other type: specify a signature that excludes T or its supertypes. There was recently a posting where negation was expressed via implication, like T => Void... I wonder...

Thanks,
-Vlad

Sébastien Bocq

unread,
Oct 14, 2011, 4:59:55 AM10/14/11
to scala-l...@googlegroups.com


2011/10/14 Oleg Zhurakousky <oleg.zhu...@gmail.com>

Just came up with this. It might prove hackish with usage but seems you can use type tags [1] and implicit to implement a (very) poor man's effect system.

scala> type Tagged[U] = { type Tag = U }
defined type alias Tagged

scala> type @@[T, U] = T with Tagged[U]
defined type alias $at$at

scala> type NotUnit = {}
defined type alias NotUnit

scala> implicit def tagBool(b:Boolean):Boolean @@ NotUnit = b.asInstanceOf[Boolean @@ NotUnit]
tagBool: (b: Boolean)@@[Boolean,NotUnit]

scala> implicit def tagAnyRef[T <: AnyRef](t:T):T @@ NotUnit = t.asInstanceOf[T @@ NotUnit]
tagAnyRef: [T <: AnyRef](t: T)@@[T,NotUnit]

scala> def m[A](f:String => A @@ NotUnit):A = f("hi!")
m: [A](f: String => @@[A,NotUnit])A

scala> m(_ == "hi!")
res6: Boolean = true

scala> m(println(_))
<console>:22: error: type mismatch;
 found   : Unit
 required: @@[?,NotUnit]
              m(println(_))
                       ^

--
Sébastien

[1] https://gist.github.com/89c9b47a91017973a35f

Miles Sabin

unread,
Oct 15, 2011, 2:42:48 PM10/15/11
to scala-l...@googlegroups.com
On Fri, Oct 14, 2011 at 9:59 AM, Sébastien Bocq
<sebasti...@gmail.com> wrote:

> 2011/10/14 Oleg Zhurakousky <oleg.zhu...@gmail.com>
>>
>> So in Java i can quickly determine if a method returns something or not by
>> simply looking at its signature.
>> In Scala we have Unit which is often mistaken for a Void since it is used
>> in places equivalent to Java void. Anyway, here is my dilemma. For example;
>> if I want to provide a function signature that ensures that the function
>> returns Boolean I can do this:
>>
>> def foo(function: String => Boolean)
>>
>> now bar.foo{m => 4 < 3} - will work fine but bar.foo{m => 4} will not.
>> Perfect.
>>
>> But how do I structure the signature of the function to make sure that the
>> following is legal bar.foo{m => m} or bar.foo{m => m.toString()} etc., but
>> NOT bar.foo{m => println("4")}
>>
>> In other words I want to enforce that the provided function code returns
>> something (at least from its structure) - non-Unit
>

> Just came up with this. It might prove hackish with usage but seems you can
> use type tags [1] and implicit to implement a (very) poor man's effect
> system.

That's probably not the best way to approach it. Try this instead,

https://gist.github.com/c9f8befa932d98dcc7a4

// Encoding for "A is not a subtype of B"
trait <:!<[A, B]

// Uses ambiguity to rule out the cases we're trying to exclude
implicit def nsub[A, B] : A <:!< B = null
implicit def nsubAmbig1[A, B >: A] : A <:!< B = null
implicit def nsubAmbig2[A, B >: A] : A <:!< B = null

// Type alias for context bound
type |¬|[T] = {
type λ[U] = U <:!< T
}

def foo[T, R : |¬|[Unit]#λ](t : T)(f : T => R) = f(t)

foo(23)(_ + 1) // OK
foo(23)(println) // Doesn't compile

Cheers,


Miles

--
Miles Sabin
tel: +44 7813 944 528
gtalk: mi...@milessabin.com
skype: milessabin
http://www.chuusai.com/
http://twitter.com/milessabin

Rex Kerr

unread,
Oct 15, 2011, 3:10:37 PM10/15/11
to scala-l...@googlegroups.com
Very clever!  I'd never thought to use ambiguity that way.
  --Rex

Sébastien Bocq

unread,
Oct 15, 2011, 4:23:24 PM10/15/11
to scala-l...@googlegroups.com


2011/10/15 Miles Sabin <mi...@milessabin.com>


That's neat indeed!

Why is it called ambiguity? And why do you need two times the the same implicit (nsubAmbig1, nsubAmbig2)?
--
Sébastien

Sébastien Bocq

unread,
Oct 15, 2011, 5:19:48 PM10/15/11
to scala-l...@googlegroups.com


2011/10/15 Sébastien Bocq <sebasti...@gmail.com>

Never mind, I got it. Even more clever than what I thought :)

Cheers,
Sébastien

Daniel Sobral

unread,
Oct 15, 2011, 8:34:32 PM10/15/11
to scala-l...@googlegroups.com
Why does nsubAmbig take precedence over nsub?

--

Miles Sabin

unread,
Oct 16, 2011, 3:54:37 AM10/16/11
to scala-l...@googlegroups.com
On Sun, Oct 16, 2011 at 1:34 AM, Daniel Sobral <dcso...@gmail.com> wrote:
> Why does nsubAmbig take precedence over nsub?

Because it's more specific that nsub according the rules given in
6.26.3 of the spec.

Reply all
Reply to author
Forward
0 new messages