Related
I implemented a toy worksheet in intellij.
abstract class Nat:
def isZero: Boolean
def predecessor: Nat
def successor: Nat
def + (that: Nat): Nat
def - (that: Nat): Nat
end Nat
object Zero extends Nat:
def isZero: Boolean = true
def predecessor: Nat = ???
def successor: Nat = Succ(this)
def + (that: Nat): Nat = that
def - (that: Nat): Nat = if that.isZero then this else ???
override def toString = "Zero"
end Zero
class Succ(n: Nat) extends Nat:
def isZero: Boolean = false
def predecessor: Nat = n
def successor: Nat = Succ(this)
def + (that: Nat): Nat = Succ(n + that)
def - (that: Nat): Nat = if that.isZero then this else n - that.predecessor
override def toString = s"Succ($n)"
end Succ
val two = Succ(Succ(Zero)) // : Succ =
val one = Succ(Zero)
two + one
two - one
//one - two
Other worksheet has been evaluated fine so far, but when I evaluated this one, I encountered some errors show below:
// defined class Nat
4 | def successor: Nat = Succ(this)
| ^^^^
| Not found: Succ
// defined class Succ
1 |val two = Succ(Succ(Zero))
| ^^^^
| Not found: Zero
1 |val one = Succ(Zero)
| ^^^^
| Not found: Zero
1 |two + one
|^^^
|Not found: two
1 |two - one
|^^^
|Not found: two
How to resolve the not found error?
The specific problem is that worksheets are executed in order and Succ has not been defined when Zero is being defined.
More generally I suggest switching to a full App rather than a worksheet. Writing and running an App is pretty east. Create a subclass of App with your code:
object Worksheet extends App {
println("Hello world")
}
Then just click the green arrow to the left of the object and IntelliJ will compile and run the program.
Or right click and select Debug so that you can put breakpoints on key points the code and then step through line by line.
Type Driven Development with Idris presents the following generic adder approach:
AdderType : (numArgs : Nat) -> Type
AdderType Z = Int
AdderType (S k) = (next : Int) -> AdderType k
adder : (n : Nat) -> (acc : Int) -> AdderType n
adder Z acc = acc
adder (S k) acc = \x => (adder k (x+acc))
Example:
-- expects 3 Int's to add, with a starting value of 0
*Work> :t (adder 3 0)
adder 3 0 : Int -> Int -> Int -> Int
-- 0 (initial) + 3 + 3 + 3 == 9
*Work> (adder 3 0) 3 3 3
9 : Int
I'm guessing that shapeless can handle the above generic adder function.
How can it be written in Scala with or without shapeless?
Update: I'll leave my original implementation below, but here's one that's a little more direct:
import shapeless._
trait AdderType[N <: Nat] extends DepFn1[Int]
object AdderType {
type Aux[N <: Nat, Out0] = AdderType[N] { type Out = Out0 }
def apply[N <: Nat](base: Int)(implicit at: AdderType[N]): at.Out = at(base)
implicit val adderTypeZero: Aux[Nat._0, Int] = new AdderType[Nat._0] {
type Out = Int
def apply(x: Int): Int = x
}
implicit def adderTypeSucc[N <: Nat](implicit
atN: AdderType[N]
): Aux[Succ[N], Int => atN.Out] = new AdderType[Succ[N]] {
type Out = Int => atN.Out
def apply(x: Int): Int => atN.Out = i => atN(x + i)
}
}
And then:
scala> val at3 = AdderType[Nat._3](0)
at3: Int => (Int => (Int => Int)) = <function1>
scala> at3(3)(3)(3)
res8: Int = 9
Original answer below.
Here's an off-the-cuff Scala translation:
import shapeless._
trait AdderType[N <: Nat] extends DepFn1[Int] {
protected def plus(x: Int): AdderType.Aux[N, Out]
}
object AdderType {
type Aux[N <: Nat, Out0] = AdderType[N] { type Out = Out0 }
def apply[N <: Nat](base: Int)(implicit at: AdderType[N]): Aux[N, at.Out] =
at.plus(base)
private[this] case class AdderTypeZero(acc: Int) extends AdderType[Nat._1] {
type Out = Int
def apply(x: Int): Int = acc + x
protected def plus(x: Int): Aux[Nat._1, Int] = copy(acc = acc + x)
}
private[this] case class AdderTypeSucc[N <: Nat, Out0](
atN: Aux[N, Out0],
acc: Int
) extends AdderType[Succ[N]] {
type Out = Aux[N, Out0]
def apply(x: Int): Aux[N, Out0] = atN.plus(acc + x)
protected def plus(x: Int): Aux[Succ[N], Aux[N, Out0]] = copy(acc = acc + x)
}
implicit val adderTypeZero: Aux[Nat._1, Int] = AdderTypeZero(0)
implicit def adderTypeSucc[N <: Nat](implicit
atN: AdderType[N]
): Aux[Succ[N], Aux[N, atN.Out]] = AdderTypeSucc(atN, 0)
}
And then:
scala> val at3 = AdderType[Nat._3](0)
at3: AdderType[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]] { ...
scala> at3(3)(3)(3)
res0: Int = 9
It's more verbose and the representation is a little different to get the Scala syntax to work out—our "base case" is essentially an Int => Int instead of an Int because otherwise I don't see a way to avoid needing to write apply or () everywhere—but the basic ideas are exactly the same.
In case you're on a long trip and don't have your shapeless on hands, here is how you can do this in pure Scala. It can be useful for those who are not familiar with shapeless and those who don't use it for some reason.
First of all, we'll need some way to iterate on types, i.e. represent natural numbers in types. You can use any nested type or just define a new one with some aliases for numbers:
sealed trait Nat
trait Zero extends Nat
trait Succ[N <: Nat] extends Nat
// enough for examples:
type _0 = Zero
type _1 = Succ[_0]
type _2 = Succ[_1]
type _3 = Succ[_2]
type _4 = Succ[_3]
// etc...
Of course, if you will often use types like _42 and _342923, it would be more convenient to take an existing Nat type with some macro-magic for constructing those from values, but for our examples it's enough.
Now, the AdderType dependent function type is quite straight forward:
// first we define the type which take a Nat type argument
trait AdderType[N <: Nat] {
type Out
def apply(i: Int): Out
}
// then we inductively construct its values using implicits
case object AdderType {
// base case: N = _0
implicit def zero:
AdderType[_0] { type Out = Int } =
new AdderType[_0] {
type Out = Int
def apply(i: Int): Out = i
}
// induction step: N -> Succ[N]
implicit def succ[N <: Nat, NOut](
implicit prev: AdderType[N] { type Out = NOut }
): AdderType[Succ[N]] { type Out = Int => NOut } =
new AdderType[Succ[N]] {
type Out = Int => NOut
def apply(i: Int): Out = k => prev(i + k)
}
}
Now, to construct an instance of AdderType and apply it, we write a function, which takes a N <: Nat as a type argument and implicitly constructs AdderType[N]:
def adder[N <: Nat](initial: Int)(
implicit adderFunction: AdderType[N]
): adderFunction.Out = adderFunction(initial)
That's it:
scala> val add3Numbers = adder_[_3](0)
add3Numbers: Int => (Int => (Int => Int)) = <function1>
scala> add3Numbers(1)(2)(3)
res0: Int = 6
You can see that the pure solution is not much bigger or more complicated than the one using shapeless (although the latter provides us ready-to-use Nat and DepFn types).
A little addition: if (in some more general case) you don't want to use adderFunction.Out, which sometimes leads to problems, I also have a solution without it. In this particular case it's not any better, but I'll show it anyway.
The key point is to add another type parameter for the out type: adder[N <: Nat, NOut], but then can't pass N as a type to adder, because we will need to write NOut, which want to be inferred (otherwise, what's the point). So we can pass an additional value argument, which will help to derive N type:
def adder[N <: Nat, NOut](n: NatVal[N])(initial: Int)(
implicit adderFunction: AdderType[N] { type Out = NOut }
): NOut = adderFunction(initial)
To construct NatVal[N] we don't need to create an instance of each Nat type, we can use a little trick:
// constructing "values" to derive its type arg
case class NatVal[N <: Nat]()
// just a convenience function
def nat[N <: Nat]: NatVal[N] = NatVal[N]()
Now here is how you use it:
scala> val add3Numbers = adder(nat[_3])(0)
add3Numbers: this.Out = <function1>
scala> add3Numbers(1)(2)(3)
res1: this.Out = 6
You can see that it works, but doesn't show us the actual types. Nevertheless, this approach can work better in cases when you have several implicits that depend others' type members. I mean
def foo[AOut]()(implicit
a: A { type Out = AOut},
b: B { type In = AOut }
) ...
instead of
def foo()(implicit
a: A,
b: B { type In = a.Out }
) ...
Because you cannot reffer to a.Out in the same argument list.
You can find full code in my repo on Github.
I'm new to Scala and need some help for resolving a compilation error:
[error] .../traversals /traversals.scala:120: type mismatch;
[error] found : Traversable[Tree]
[error] required: Traversable[Node]
[error] Note: Tree >: Node, but trait Traversable is invariant in type T.
[error] You may wish to define T as -T instead. (SLS 4.5)
[error] println ("Sorted " + sorted (tree2) (monadApp,inOrder));
[error] ^
[error] one error found
I'm sorry that the MWE is that long, but I translated some type classes in a naive way from Haskell to Scala and stuck when I want to write some example that makes use of them.
I don't understand the problem exactly, but it seems as if either my Traversable trait does not allow that the T is replaced by some subtype or the concrete instance inOrder or the function sorted which uses the instance. As suggested by the compiler I tried to add some -s in front of the T at the Traversable trait, the Tree in front of the inOrder definition or the T in front of sorted, but it didn't help.
trait Applicative[M[_]] {
def pure[a] (x: a) : M[a]
def comp[a,b] (fx: M[a => b]) (mx: M[a]) : M[b]
def fmap[a,b] (fx: a => b) (mx: M[a]) : M[b]
= comp (pure (fx)) (mx)
}
trait Functor[F[_]] {
def fmap[a,b] (f: a => b) (m: F[a]) : F[b]
}
trait Traversable[T[_]] extends Functor[T] {
def dist[a,M[_]] (t: T[M[a]]) (implicit app : Applicative[M]) : M[T[a]]
def traverse[a,b,M[_]] (f: a => M[b]) (t : T[a]) (implicit app : Applicative[M]) : M[T[b]] =
dist (fmap(f) (t)) (app)
}
sealed abstract class Tree[a]
case class Empty[a] () extends Tree[a]
case class Node[a] (x : a, l : Tree[a], r: Tree[a]) extends Tree[a]
class TreeFunctor extends Functor[Tree] {
def fmap[a,b] (f: a => b) (t: Tree[a]) =
t match {
case Empty () => Empty ()
case Node (x, l, r) => Node (f (x), fmap (f) (l), fmap (f) (r))
}
}
trait Monoid[A] {
def mempty : A
def mappend (x: A) (y: A) : A
}
object BoolAnd extends Monoid[Boolean] {
def mempty = true
def mappend (x: Boolean) (y: Boolean) = x && y
}
case class K[b,a] (value: b)
class MonoidApplicative[m] (implicit monoid : Monoid[m]) extends Applicative[({type λ[α] = K[m,α]})#λ] {
def pure[a] (x : a) = K (monoid.mempty)
def comp[a,b] (f : K[m,a => b]) (x : K[m,a]) = K (monoid.mappend (f.value) (x.value))
}
case class State[s,a] (func: s => (a,s))
trait Monad[M[_]] {
def ret[a] (x : a) : M[a]
def bind[a,b] (mx : M[a]) (fx : a => M[b]) : M[b]
}
class StateMonad[S] extends Monad[({type λ[α] = State[S,α]})#λ] {
def ret[a] (x : a) = State ((s: S) => (x, s))
def bind[a,b] (mx : State[S,a]) (fx : a => State[S,b])
= State ((s : S) => (((tuple : (a,S)) => (fx (tuple._1)).func (tuple._2)) (mx.func (s))))
}
class MonadApp[M[_]] (implicit m : Monad[M]) extends Applicative[M] {
def pure[a] (x : a) = m.ret (x)
def comp[a,b] (fx : M[a => b]) (mx : M[a]) : M[b]
= m.bind[a => b,b] (fx) ((f : a => b) => m.bind[a,b] (mx) ((x:a) => m.ret (f (x))))
}
case class Comp[M[_],N[_],a] (unComp : M[N[a]])
class CompApp[M[_], N[_]] (implicit mapp : Applicative[M], napp: Applicative[N]) extends Applicative[({type λ[α] = Comp[M,N,α]})#λ] {
def pure[a] (x : a) = Comp (mapp.pure ( napp.pure ( x) ))
def comp[a,b] (mf : Comp[M,N,a => b]) (mx : Comp[M,N,a]) = Comp (mapp.comp (mapp.comp (mapp.pure (napp.comp[a,b]_)) (mf.unComp)) (mx.unComp) )
}
object Main {
implicit def inOrder : Traversable[Tree] = new Traversable[Tree]{
def dist[a,M[+_]] (t: Tree[M[a]]) (implicit app : Applicative[M])
= t match {
case Empty () => app.pure (Empty ())
case Node (x, l, r) => app.comp (app.comp (app.comp(app.pure ((l : Tree[a]) => ((x: a) => ((r: Tree[a]) => Node (x,l,r))))) (dist (l) (app))) (x)) (dist (r) (app))
}
}
val emptyTree = Empty[Int]()
val tree2 = Node(5, Node (2, Empty (), Empty ()), Node (9 , Empty (), Empty ()))
implicit def stateMonad[a] = new StateMonad[a] ()
implicit def monadApp = new MonadApp[({type λ[α] = State[Int,α]})#λ] () {}
implicit def andMonoidApp = new MonoidApplicative[Boolean] () (BoolAnd);
implicit def stateMonoidComp = new CompApp[({type κ[β] = State[Int,β]})#κ, ({type λ[α] = K[Boolean,α]})#λ ] () (monadApp, andMonoidApp)
def pairSort (x : Int) : State[Int,Boolean]
= State ((y : Int) => (y <= x, x))
def sorted[T[_]] (t : T[Int]) (implicit as : Applicative[({type λ[α] = State[Int,α]})#λ], tr : Traversable[T]) : Boolean
= (
(tr.traverse[Int,Boolean,({type λ[α] = Comp[({type κ[β] = State[Int,β]})#κ, ({type λ[α] = K[Boolean,α]})#λ , α]})#λ]
((i : Int) =>
Comp[({type κ[β] = State[Int,β]})#κ, ({type λ[α] = K[Boolean,α]})#λ , Boolean]
(as.fmap[Boolean, K[Boolean,Boolean]] ((x: Boolean) => K[Boolean, Boolean] (x)) (pairSort (i)))
)
(t)
//(CompApp[({type κ[β] = State[Int,β]})#κ, ({type λ[α] = K[Boolean,α]})#λ ])
(stateMonoidComp)
).unComp.func (-10000)
)._1.value
def main (args: Array[String])
= println ("Sorted " + sorted (tree2) (monadApp,inOrder));
}
The problem is that you have two different types: Traversable[Node] and Traversable[Tree]. This comes from the ADT translation from Haskell. Whereas in Haskell Node and Empty are both Tree, in Scala they are subtypes of Tree, which causes the concept of variance to come into play: given A subtype of B, is T[A] a subtype of T[B]?
If we look at the type of a function X => Y, we'll see it is declared Function1[-X, +Y], that is, it is contravariant in X and covariant in Y. That means a function that takes Tree and returns Node is a subtype of a function that takes Node and returns Tree. For example:
def f[T](n: Node[T])(func: Node[T] => Tree[T]): Tree = func(n)
def zeroRoot(tree: Tree[Int]): Node[Int] = Node(0, tree, Empty())
f(Node(1, Empty(), Empty())(zeroRoot)
The function zeroRoot works, because we are passing it a Node and it expected a Tree (which is ok), and we returned a Node, which in turn was returned as a Tree (also ok).
Incidentally, Tree should probably be co-variant.
Now, one other example of contra-variance is the ordering class. While Scala's invariant because of some technicalities, a proper ordering should be contra-variant. After all, if you know how to order a Tree, it follows that you can order a Node, so an Ordering[Tree] would be a subtype of Ordering[Node], since the first can be passed where the second is expected.
I haven't really paid close attention to the code, but it makes sense that something used as ordering ought to be contra-variant. On the other hand, it doesn't seem possible to make Traversable contravariant, since Functor type signature requires invariance.
Fix this problem by passing the type Tree explicitly to Sort, or declare tree2 explicitly as a Tree. Then you'll get other, different problems! :)
I have a workflow like this:
parse template -> check consistency
-> check conformance of one template to another
parse template -> check consistency
Either one of those steps may fail. I would like to implement that in Scala, preferably so that the parallel branches get evaluated independently merging both their errors. Perhaps in a monadic style but I am curious about some general OOP pattern too. Currently I have multiple variations hardcoded for various actions with the chaining like this
def loadLeftTemplateAndForth (leftPath : String, rightPath : String) = {
val (template, errors) = loadTemplate(leftPath)
if(errors.isEmpty) loadRightTemplateAndForth(template, rightPath)
else popupMessage("Error.")
}
which I bet must be some kind of antipattern. The steps need decoupling from the workflow but I was not able to come up with anything extremely elegant and there must proven ways already.
EDIT:
Ok, so I have unsuccessfully tried to implement something like this
(((parseTemplate(path1) :: HNil).apply(checkConsistency _) :: ((parseTemplate(path2) :: HNil).apply(checkConsistency _)) :: HNil).apply(checkConformance _)
def checkConformance (t1 : Template)(t2 : Template) : Seq[Error]
The functions would then return Success(result) or Failure(errors). I was using HLists but got lost in the type inference rules and other issues. It seems I was pretty close though. For someone knowledgable of this stuff it would probably be a piece of cake.
EDIT:
I have finally managed to implement this
(parseTemplate("Suc") :: Args).apply(checkConsistency _) ::
(parseTemplate("Suc") :: Args).apply(checkConsistency _) :: Args)
.apply(checkConformance _)
with some unfornate constraints that each function must return my equivalent of Either and that the error type of applied function must be a subtype of arguments' error type. I did it using HList, application typeclass and a wrapper class Successful/UnsuccessfulArgList.
How about this?
// Allows conditional invocation of a method
class When[F](fun: F) {
def when(cond: F => Boolean)(tail: F => F) =
if (cond(fun)) tail(fun) else fun
}
implicit def whenever[F](fun: F): When[F] = new When[F](fun)
After that:
parseTemplate(t1).when(consistent _){
val parsed1 = _
parseTemplate(t2).when(consistent _){
conforms(parsed1, _)
}
}
Create some holder for errors, and pass it around (to parseTemplate, to consistent, to conforms), or use ThreadLocal.
Here is decoupled much more:
(parseTemplate(t1), parseTemplate(t2))
.when(t => consistent(t._1) && consistent(t._2)){ t =>
conforms(t._1, t._2)
}
EDIT
I've ended up with something like this:
def parse(path: String): Either[
String, // error
AnyRef // result
] = ?
def consistent(result: Either[String, AnyRef]): Either[
String, // error
AnyRef // result
] = ?
def conforms(result1: Either[String, AnyRef], result2: Either[String, AnyRef],
fullReport: List[Either[
List[String], // either list of errors
AnyRef // or result
]]): List[Either[List[String], AnyRef]] = ?
( (parse("t1") :: Nil).map(consistent _),
(parse("t2") :: Nil).map(consistent _)
).zipped.foldLeft(List[Either[List[String], AnyRef]]())((fullReport, t1t2) =>
conforms(t1t2._1, t1t2._2, fullReport))
Have your loadTemplate methods return Either[List[String], Template].
For errors return Left(List("error1",...)) and for success return Right(template).
Then you can do
type ELT = Either[List[String], Template]
def loadTemplate(path: String): ELT = ...
def loadRightTemplateAndForth(template: Template, rightPath: String): ELT = ...
def loadLeftTemplateAndForth(leftPath: String, rightPath: String): ELT =
for {
lt <- loadTemplate(leftPath).right
rt <- loadRightTemplateAndForth(lt, rightPath).right
} yield rt
The above is "fail fast", that is, it won't merge errors from the two branches. If the first fails it will return a Left and won't evaluate the second. See this project for code to handle error accumulation using Either.
Alternatively you can use Scalaz Validation. See Method parameters validation in Scala, with for comprehension and monads for a good explanation.
So the way I managed to do it is this (It still could use a refinements though - for example so that it constructs sequence of errors with type common to the list errors and function errors):
HList.scala
import HList.::
sealed trait HList [T <: HList[T]] {
def ::[H1](h : H1) : HCons[H1, T]
}
object HList {
type ::[H, T <: HList[T]] = HCons[H, T]
val HNil = new HNil{}
}
final case class HCons[H, T <: HList[T]](head: H, tail: T) extends HList[HCons[H, T]] {
override def ::[H1](h: H1) = HCons(h, this)
def apply[F, Out](fun : F)(implicit app : HApply[HCons[H, T], F, Out]) = app.apply(this, fun)
override def toString = head + " :: " + tail.toString
None
}
trait HNil extends HList[HNil] {
override def ::[H1](h: H1) = HCons(h, this)
override def toString = "HNil"
}
HListApplication.scala
#implicitNotFound("Could not find application for list ${L} with function ${F} and output ${Out}.")
trait HApply[L <: HList[L], -F, +Out] {
def apply(l: L, f: F): Out
}
object HApply {
import HList.::
implicit def happlyLast[H, Out] = new HApply[H :: HNil, H => Out, Out] {
def apply(l: H :: HNil, f: H => Out) = f(l.head)
}
implicit def happlyStep[H, T <: HList[T], FT, Out](implicit fct: HApply[T, FT, Out]) = new HApply[H :: T, H => FT, Out] {
def apply(l: H :: T, f: H => FT) = fct(l.tail, f(l.head))
}
}
ErrorProne.scala
sealed trait ErrorProne[+F, +S]
case class Success [+F, +S] (result : S) extends ErrorProne[F, S]
case class Failure [+F, +S] (errors : Seq[F]) extends ErrorProne[F, S]
ArgList.scala
import HList.::
import HList.HNil
sealed trait ArgList [E, L <: HList[L]] {
def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]])
: ErrorProne[E, S]
def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L]
}
case class SuccessArgList [E, L <: HList[L]] (list : L) extends ArgList[E, L] {
def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]])
: ErrorProne[E, S] = app.apply(list, fun)
override def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L] = argument match {
case Success(a) => SuccessArgList(a :: list)
case Failure(e) => FailureArgList(e)
}
}
case class FailureArgList [E, L <: HList[L]] (errors : Seq[E]) extends ArgList[E, L] {
def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]])
: ErrorProne[E, S] = Failure(errors)
override def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L] = argument match {
case Success(a) => FailureArgList(errors)
case Failure(newErrors) => FailureArgList(Seq[EX]() ++ errors ++ newErrors)
}
}
object Args {
def :: [E1, A] (argument : ErrorProne[E1, A]) : ArgList[E1, A :: HNil] = argument match {
case Success(a) => SuccessArgList(a :: HNil)
case Failure(e) => FailureArgList(e)
}
}
Usage
val result = ((parseTemplate("Suc") :: Args).apply(checkConsistency _) ::
(parseTemplate("Suc") :: Args).apply(checkConsistency _) :: Args)
.apply(checkConformance _)
trait Err
case class Err1 extends Err
case class Err2 extends Err
case class Err3 extends Err
def parseTemplate(name : String) : ErrorProne[Err, Int] = if(name == "Suc") Success(11) else Failure(Seq(Err1()))
def checkConsistency(value : Int) : ErrorProne[Err2, Double] = if(value > 10) Success(0.3) else Failure(Seq(Err2(), Err2()))
def checkConformance(left : Double) (right : Double) : ErrorProne[Err3, Boolean] =
if(left == right) Success(true) else Failure(Seq(Err3()))
Let's say we have the following types:
sealed trait T
case object Goat extends T
case object Monk extends T
case object Tiger extends T
Now, how do you construct a collection of T such that at least one of each sub-T appears in the collection, this constraint being enforced at compile time? A collection where, contrary to this:
val s:Seq[T] = Seq(Goat)
which compiles, this:
val ts:TotalSeq[T] = TotalSeq(Goat, Goat, Tiger)
does not. I've used Scala above, but I would be happy to see solutions in other languages as well.
(Forgive me if my words are a bit off; I have a cold and am amusing myself today with puzzles.)
It looks like the builder pattern with generalized type constraints: http://www.tikalk.com/java/blog/type-safe-builder-scala-using-type-constraints
Something like:
sealed trait TBoolean
sealed trait TTrue extends TBoolean
sealed trait TFalse extends TBoolean
class SeqBuilder[HasGoat <: TBoolean, HasMonk <: TBoolean, HasTiger <: TBoolean] private (seq: Seq[T]) {
def +=(g: Goat.type) = new SeqBuilder[TTrue, HasMonk, HasTiger](g +: seq)
def +=(m: Monk.type) = new SeqBuilder[HasGoat, TTrue, HasTiger](m +: seq)
def +=(t: Tiger.type) = new SeqBuilder[HasGoat, HasMonk, TTrue](t +: seq)
def build()(implicit evg: HasGoat =:= TTrue, evm: HasMonk =:= TTrue, evt: HasTiger =:= TTrue) = seq
}
object SeqBuilder {
def apply = new SeqBuilder(Seq())
}
Usage:
scala> SeqBuilder() + Goat + Tiger + Monk build()
res21: Seq[T] = List(Monk, Tiger, Goat)
scala> SeqBuilder() + Goat + Goat + Monk build()
<console>:34: error: Cannot prove that Nothing =:= TTrue.
SeqBuilder() + Goat + Goat + Monk build()
^
If the number of instances you want grows you can try using an HMap
Other similar approaches: phantom types: http://james-iry.blogspot.com/2010/10/phantom-types-in-haskell-and-scala.html
Another approach is to ask why you need all 3 objects. Say you want to carry an operation when the seq has all three, then you can do something like:
object SeqBuilder {
val all = Seq(Goat, Monk, Tiger)
def apply(t: T*)(onAll: Seq[T] => Unit)(onViolation: => Unit) = {
val seq = Seq(t:_*)
if(all forall seq.contains) onAll(seq)
else onViolation
}
}
That way, the function onAll is not called if not all required Ts have been supplied and otherwise the violation function is called
If I'm interpreting this correctly...
{-# LANGUAGE GADTs #-}
{-# LANGUAGE EmptyDataDecls #-}
data Goat
data Monk
data Tiger
data T a where
TGoat :: T Goat
TMonk :: T Monk
TTiger :: T Tiger
data TSeq a where
TNil :: TSeq ((), (), ())
TG :: T Goat -> TSeq ((), m, t) -> TSeq (Goat, m, t)
TM :: T Monk -> TSeq (g, (), t) -> TSeq (g, Monk, t)
TT :: T Tiger -> TSeq (g, m, ()) -> TSeq (g, m, Tiger)
TCG :: T Goat -> TSeq (Goat, m, t) -> TSeq (Goat, m, t)
TCM :: T Monk -> TSeq (g, Monk, t) -> TSeq (g, Monk, t)
TCT :: T Tiger -> TSeq (g, m, Tiger) -> TSeq (g, m, Tiger)
Be aware that no attempt has been made to make this usable. It's incredibly clumsy.
Now, given these values:
x = TCG TGoat (TG TGoat (TT TTiger (TM TMonk TNil)))
y = TG TGoat TNil
And this function:
f :: TSeq (Goat, Monk, Tiger) -> a
f _ = undefined
...then only f x will type check, not f y.
N.B. Removing items from the sequence is left as an exercise for the reader.
EDIT: Upon further consideration I've decided the above is not at all terrible enough. Behold:
Some mysteriously familiar boilerplate:
data Yes = Yes
data No = No
class TypeEq' () x y b => TypeEq x y b | x y -> b
instance TypeEq' () x y b => TypeEq x y b
class TypeEq' q x y b | q x y -> b
class TypeEq'' q x y b | q x y -> b
instance TypeCast b Yes => TypeEq' () x x b
instance TypeEq'' q x y b => TypeEq' q x y b
instance TypeEq'' () x y No
class TypeCast a b | a -> b, b->a where typeCast :: a -> b
class TypeCast' t a b | t a -> b, t b -> a where typeCast' :: t->a->b
class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b
instance TypeCast' () a b => TypeCast a b where typeCast x = typeCast' () x
instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast''
instance TypeCast'' () a a where typeCast'' _ x = x
A bit of general hackery:
infixr 1 :*:
data NIL
data h :*: t = h :*: t
class TIns x ys r | x ys -> r
instance TIns x NIL (x :*: NIL)
instance ( TypeEq x h b
, TIns' b x h t r
) => TIns x (h :*: t) r
class TIns' b x h t r | b x h t -> r
instance TIns' Yes x h t (h :*: r)
instance (TIns x t r) => TIns' No x h t (h :*: r)
class TElem x ys r | x ys -> r
instance TElem x NIL No
instance (TypeEq x h b, TElem' b x h t r) => TElem x (h :*: t) r
class TElem' b x h t r | b x h t -> r
instance TElem' Yes x h t Yes
instance (TElem x t r) => TElem' No x h t r
Aaaaaaaand the payoff:
data TSeq2 a b where
TNil2 :: TSeq2 NIL NIL
TCons2 :: (TIns x ys r) => T x -> TSeq2 xs ys -> TSeq2 (x :*: xs) r
class (TElem x ys Yes) => Has ys x
instance (TElem x ys Yes) => Has ys x
class HasAll ys xs
instance HasAll ys NIL
instance (ys `Has` h, ys `HasAll` t) => HasAll ys (h :*: t)
x2 = TCons2 TGoat (TCons2 TGoat (TCons2 TTiger (TCons2 TMonk TNil2)))
y2 = TCons2 TGoat TNil2
f2 :: (s `HasAll` (Goat :*: Tiger :*: Monk :*: NIL)) => TSeq2 q s -> a
f2 _ = undefined
As with the above, f2 x2 typechecks, while f2 y2 fails.
This is still messy and rather painful to use, but far more generic!
And just to prove that the result can still be treated as an ordinary data type in other circumstances, here's an instance of Show:
instance Show (T a) where
show TGoat = "TGoat"
show TMonk = "TMonk"
show TTiger = "TTiger"
instance Show (TSeq2 a b) where
show TNil2 = "[]"
show (TCons2 x xs) = concat [show x, ":", show xs]
So now we can do things like show only sequences that contain all three kinds of item:
showTotal :: (s `HasAll` (Goat :*: Tiger :*: Monk :*: NIL)) => TSeq2 q s -> String
showTotal = show
You can't do it with exactly the syntax you've proposed, but you can get close; the only compromise you have to make is using an operator instead of ,. I've chosen ~, and since you didn't exactly specify the behavior when the type is not specified on the left, I've made something vaguely sensible.
The code is a headache, however. Haskell is better at concisely expressing this; the strategy is similar to what camccann has already provided.
object Example {
sealed trait T
case object Goat extends T
case object Monk extends T
case object Tiger extends T
implicit def Gx(g: Goat.type) = new Goats(Seq(g))
implicit def Mx(m: Monk.type) = new Monks(Seq(m))
implicit def Tx(t: Tiger.type) = new Tigers(Seq(t))
trait Combo { def s: Seq[T] }
case class Goats(s: Seq[T]) extends Combo {
def ~(g: Goat.type) = Goats(s :+ g)
def ~(m: Monk.type) = GoatMonk(s :+ m)
def ~(t: Tiger.type) = GoatTiger(s :+ t)
}
case class Monks(s: Seq[T]) extends Combo {
def ~(g: Goat.type) = GoatMonk(s :+ g)
def ~(m: Monk.type) = Monks(s :+ m)
def ~(t: Tiger.type) = MonkTiger(s :+ t)
}
case class Tigers(s: Seq[T]) extends Combo {
def ~(g: Goat.type) = GoatTiger(s :+ g)
def ~(m: Monk.type) = MonkTiger(s :+ m)
def ~(t: Tiger.type) = Tigers(s :+ t)
}
case class GoatMonk(s: Seq[T]) extends Combo {
def ~(g: Goat.type) = GoatMonk(s :+ g)
def ~(m: Monk.type) = GoatMonk(s :+ m)
def ~(t: Tiger.type) = GoatMonkTiger(s :+ t)
}
case class GoatTiger(s: Seq[T]) extends Combo {
def ~(g: Goat.type) = GoatTiger(s :+ g)
def ~(m: Monk.type) = GoatMonkTiger(s :+ m)
def ~(t: Tiger.type) = GoatTiger(s :+ t)
}
case class MonkTiger(s: Seq[T]) extends Combo {
def ~(g: Goat.type) = GoatMonkTiger(s :+ g)
def ~(m: Monk.type) = MonkTiger(s :+ m)
def ~(t: Tiger.type) = MonkTiger(s :+ t)
}
case class GoatMonkTiger(s: Seq[T]) {
def ~(t: T) = GoatMonkTiger(s :+ t)
}
class TotalSeq[A](val seq: Seq[A]) {}
implicit def total_to_regular[A](ts: TotalSeq[A]) = ts.seq
object TotalSeq {
def apply(t: T) = Seq(t)
def apply(co: Combo) = co.s
def apply(gmt: GoatMonkTiger) = new TotalSeq(gmt.s)
}
}
Let's see how we can use this:
scala> import Example._
import Example._
scala> val mmm = TotalSeq(Monk ~ Monk ~ Monk)
mmm: Seq[Example.T] = List(Monk, Monk, Monk)
scala> val mmm2: Seq[T] = TotalSeq(Monk ~ Monk ~ Monk)
mmm2: Seq[Example.T] = List(Monk, Monk, Monk)
scala> val mmm3: TotalSeq[T] = TotalSeq(Monk ~ Monk ~ Monk)
<console>:11: error: type mismatch;
found : Example.Monks
required: Example.GoatMonkTiger
val mmm3: TotalSeq[T] = TotalSeq(Monk ~ Monk ~ Monk)
^
scala> TotalSeq(Tiger ~ Goat ~ Goat ~ Goat)
res5: Seq[Example.T] = List(Tiger, Goat, Goat, Goat)
scala> TotalSeq(Goat ~ Goat ~ Tiger ~ Monk)
res6: Example.TotalSeq[Example.T] = Example$TotalSeq#5568db68
scala> val gmt: TotalSeq[T] = TotalSeq(Goat ~ Goat ~ Tiger ~ Monk)
gmt: Example.TotalSeq[Example.T] = Example$TotalSeq#f312369
I have reduced it to two subclasses to make it clearer and shorter, but the principle can be applied to any number of subclasses. Unfortunately though, it gets quite long for many subclasses.
sealed trait T
case class Monk extends T
case class Tiger extends T
class BaseList(val head:T, val tail:BaseList) {
def ::(m:Monk) = new BaseList(m, this) with ListWithMonk
def ::(t:Tiger) = new BaseList(t, this) with ListWithTiger
}
object Empty extends BaseList(null, null)
trait ListWithMonk extends BaseList {
self:BaseList =>
override def ::(t:Tiger) = new BaseList(t, self) with ListWithBoth
}
trait ListWithTiger extends BaseList {
self:BaseList =>
override def ::(m:Monk) = new BaseList(m, self) with ListWithBoth
}
trait ListWithBoth extends ListWithMonk with ListWithTiger
object Main {
def main(args:Array[String]) {
val l:ListWithBoth = new Monk :: new Tiger :: Empty
val l2:ListWithBoth = new Tiger :: new Monk :: Empty
// does not compile: val l3:ListWithBoth = new Tiger :: new Tiger :: Empty
}
}