Without keyword in scala

I recently found out that in scala we can define types like this

def burp(animal: Animal with Food)

This means that burp only accepts a type of animal with Food in it

Would it be great to have a feature like without

def eatRabbit(animal: Animal without Veg)

In this case eatRabbit will only accept a type of Animal that hasnt implemented NonVeg

You can express this with a shapeless type inequality that requires proof that a type argument does not conform with a supertype.

scala> import shapeless._
import shapeless._

scala> trait Animal; trait Veg; class Foo extends Animal;  class Bar extends Animal with Veg
defined trait Animal
defined trait Veg
defined class Foo
defined class Bar

scala> def blah[A <: Animal](a: A)(implicit ev: A <:!< Veg): A = a
blah: [A <: Animal](a: A)(implicit ev: shapeless.<:!<[A,Veg])A

scala> blah(new Foo)
res3: Foo = Foo@29eef106

scala> blah(new Bar)
<console>:20: error: ambiguous implicit values:
 both method nsubAmbig1 in package shapeless of type [A, B >: A]=> shapeless.package.<:!<[A,B]
 and method nsubAmbig2 in package shapeless of type [A, B >: A]=> shapeless.package.<:!<[A,B]
 match expected type shapeless.<:!<[Bar,Veg]
       blah(new Bar)
           ^

Of course, you​ can just upcast to Animal and bypass the constraint…

Of course, you​ can just upcast to Animal and bypass the constraint…

By way of demonstration, continuing @tpolecat’s example:

scala> blah(new Bar)

:17: error: ambiguous implicit values:

both method nsubAmbig1 in package shapeless of type [A, B >: A]=> A <:!< B

and method nsubAmbig2 in package shapeless of type [A, B >: A]=> A <:!< B

match expected type Bar <:!< Veg

    blah(new Bar)

        ^

scala> blah(new Bar: Animal)

res2: Animal = Bar@7c33e49a

So with respect to classes of values, use of <:!< should be considered
advisory, or an aid to [disambiguating] implicit resolution, not a
guarantee. Of course it’s entirely correct when considered on the level
it’s trying to operate on, types not classes: the /type/ Animal indeed
is not <: Veg, and so the second call compiles.

There is a good theoretical reason why this /must/ be so, @fhalde, which
I have elided here.

Of course, you​ can just upcast to Animal and bypass the constraint…

I think it would only be useful in cases where the type variable appears in the return type, so you would be forced to give something up by upcasting. But I can’t think of a use case offhand … my point was simply that it is already expressible.