I am trying to do the following in as little code as possible and as functionally as possible:
def restrict(floor : Option[Double], cap : Option[Double], amt : D
Not prettier, not much shorter, and certainly not faster! But it is more composable more generic and more "functional":
EDIT: made the code fully generic :)
def optWith[T](a: Option[T], b: T)(op:(T,T)=>T) =
a map (op(b,_)) getOrElse b
def optMin[T:Numeric](a: Option[T]) =
(b:T) => optWith(a, b)(implicitly[Numeric[T]].min)
def optMax[T:Numeric](a: Option[T]) =
(b:T) => optWith(a, b)(implicitly[Numeric[T]].max)
def restrict[T,FT,CT](x:T, floor:Option[FT], ceil:Option[CT])
(implicit ev:Numeric[T], fv:FT=>T, cv:CT=>T) =
optMin(ceil map cv) compose optMax(floor map fv) apply(x)
UPDATE 2: There's also this version, taking better advantage of Numeric
def optWith[T](a: Option[T])(op:(T,T)=>T) =
(b:T) => a map (op(b,_)) getOrElse b
def restrict[T,FT,CT](x:T, floor:Option[FT], ceil:Option[CT])
(implicit n:Numeric[T], fv:FT=>T, cv:CT=>T) =
optWith(ceil map cv)(n.min) compose optWith(floor map fv)(n.max) apply(x)
I hope you like type signatures :)
UPDATE 3: Here's one that does the same with bounds
def optWith[T, V <% T](op:(T,T)=>T)(a: Option[V]) =
(b:T) => a map (op(b,_)) getOrElse b
def restrict[T:Numeric, FT <% T, CT <% T]
(floor:Option[FT], ceil:Option[CT], amt:T) = {
val n = implicitly[Numeric[T]]; import n._
optWith(min)(ceil) compose
optWith(max)(floor) apply(amt)
}
If nothing else... this shows quite clearly why import parameters would be a Good Thing(tm). Imagine if the following was valid code:
def optWith[T, V <% T](op:(T,T)=>T)(a: Option[V]) =
(b:T) => a map (op(b,_)) getOrElse b
def restrict[import T:Numeric,FT <% T,CT <% T]
(floor:Option[FT], ceil:Option[CT], amt:T) = {
optWith(min)(ceil) compose
optWith(max)(floor) apply(amt)
}
UPDATE 4: Turning the solution upside down here. This one offers some more interesting possibilities for future extension.
implicit def optRhs[T:Ordering](lhs:T) = new Object {
val ord = implicitly[Ordering[T]]; import ord._
private def opt(op: (T,T)=>T)(rhs:Option[T]) =
rhs map (op(lhs,_)) getOrElse lhs
def max = opt(ord.max) _
def min = opt(ord.min) _
}
def restrict[T : Ordering](floor:Option[T], cap:Option[T], amt:T) =
amt min cap max floor
With any luck, I'll inspire someone else to build a better solution out of mine. That's how these things usually work out...