diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 3bb88e8789..3fbd5ae338 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -116,7 +116,7 @@ object OptionT extends OptionTInstances { */ def fromOption[F[_]]: FromOptionPartiallyApplied[F] = new FromOptionPartiallyApplied - class FromOptionPartiallyApplied[F[_]] private[OptionT] { + final class FromOptionPartiallyApplied[F[_]] private[OptionT] { def apply[A](value: Option[A])(implicit F: Applicative[F]): OptionT[F, A] = OptionT(F.pure(value)) } diff --git a/core/src/main/scala/cats/data/Prod.scala b/core/src/main/scala/cats/data/Prod.scala index c3c3a20c25..c395a722e4 100644 --- a/core/src/main/scala/cats/data/Prod.scala +++ b/core/src/main/scala/cats/data/Prod.scala @@ -33,35 +33,35 @@ private[data] sealed abstract class ProdInstances extends ProdInstances0 { } } -sealed abstract class ProdInstances0 extends ProdInstances1 { +private[data] sealed abstract class ProdInstances0 extends ProdInstances1 { implicit def prodMonoidK[F[_], G[_]](implicit FF: MonoidK[F], GG: MonoidK[G]): MonoidK[Lambda[X => Prod[F, G, X]]] = new ProdMonoidK[F, G] { def F: MonoidK[F] = FF def G: MonoidK[G] = GG } } -sealed abstract class ProdInstances1 extends ProdInstances2 { +private[data] sealed abstract class ProdInstances1 extends ProdInstances2 { implicit def prodSemigroupK[F[_], G[_]](implicit FF: SemigroupK[F], GG: SemigroupK[G]): SemigroupK[Lambda[X => Prod[F, G, X]]] = new ProdSemigroupK[F, G] { def F: SemigroupK[F] = FF def G: SemigroupK[G] = GG } } -sealed abstract class ProdInstances2 extends ProdInstances3 { +private[data] sealed abstract class ProdInstances2 extends ProdInstances3 { implicit def prodApplicative[F[_], G[_]](implicit FF: Applicative[F], GG: Applicative[G]): Applicative[Lambda[X => Prod[F, G, X]]] = new ProdApplicative[F, G] { def F: Applicative[F] = FF def G: Applicative[G] = GG } } -sealed abstract class ProdInstances3 extends ProdInstances4 { +private[data] sealed abstract class ProdInstances3 extends ProdInstances4 { implicit def prodApply[F[_], G[_]](implicit FF: Apply[F], GG: Apply[G]): Apply[Lambda[X => Prod[F, G, X]]] = new ProdApply[F, G] { def F: Apply[F] = FF def G: Apply[G] = GG } } -sealed abstract class ProdInstances4 { +private[data] sealed abstract class ProdInstances4 { implicit def prodFunctor[F[_], G[_]](implicit FF: Functor[F], GG: Functor[G]): Functor[Lambda[X => Prod[F, G, X]]] = new ProdFunctor[F, G] { def F: Functor[F] = FF def G: Functor[G] = GG diff --git a/core/src/main/scala/cats/data/StreamingT.scala b/core/src/main/scala/cats/data/StreamingT.scala index 86c2734509..4bc2aa7a5c 100644 --- a/core/src/main/scala/cats/data/StreamingT.scala +++ b/core/src/main/scala/cats/data/StreamingT.scala @@ -294,9 +294,9 @@ object StreamingT extends StreamingTInstances { * and Always). The head of `Cons` is eager -- a lazy head can be * represented using `Wait(Always(...))` or `Wait(Later(...))`. */ - private[cats] case class Empty[F[_], A]() extends StreamingT[F, A] - private[cats] case class Wait[F[_], A](next: F[StreamingT[F, A]]) extends StreamingT[F, A] - private[cats] case class Cons[F[_], A](a: A, tail: F[StreamingT[F, A]]) extends StreamingT[F, A] + private[cats] final case class Empty[F[_], A]() extends StreamingT[F, A] + private[cats] final case class Wait[F[_], A](next: F[StreamingT[F, A]]) extends StreamingT[F, A] + private[cats] final case class Cons[F[_], A](a: A, tail: F[StreamingT[F, A]]) extends StreamingT[F, A] /** * Create an empty stream of type A. @@ -399,7 +399,7 @@ object StreamingT extends StreamingTInstances { * evaluated. */ object syntax { - implicit class StreamingTOps[F[_], A](rhs: => StreamingT[F, A]) { + implicit final class StreamingTOps[F[_], A](rhs: => StreamingT[F, A]) { def %::(a: A)(implicit ev: Applicative[F]): StreamingT[F, A] = Cons(a, ev.pureEval(Always(rhs))) def %:::(s: StreamingT[F, A])(implicit ev: Monad[F]): StreamingT[F, A] = @@ -410,7 +410,7 @@ object StreamingT extends StreamingTInstances { Wait(fs.map(_ concat ev.pureEval(Always(rhs)))) } - implicit class FStreamingTOps[F[_], A](rhs: F[StreamingT[F, A]]) { + implicit final class FStreamingTOps[F[_], A](rhs: F[StreamingT[F, A]]) { def %::(a: A): StreamingT[F, A] = Cons(a, rhs) def %:::(s: StreamingT[F, A])(implicit ev: Monad[F]): StreamingT[F, A] = diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 3669119de7..3a95708e57 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -10,7 +10,7 @@ import cats.functor.Bifunctor * `XorT[F, A, B]` wraps a value of type `F[A Xor B]`. An `F[C]` can be lifted in to `XorT[F, A, C]` via `XorT.right`, * and lifted in to a `XorT[F, C, B]` via `XorT.left`. */ -case class XorT[F[_], A, B](value: F[A Xor B]) { +final case class XorT[F[_], A, B](value: F[A Xor B]) { def fold[C](fa: A => C, fb: B => C)(implicit F: Functor[F]): F[C] = F.map(value)(_.fold(fa, fb)) diff --git a/core/src/main/scala/cats/syntax/bifunctor.scala b/core/src/main/scala/cats/syntax/bifunctor.scala index 028fa5c442..b22c50768e 100644 --- a/core/src/main/scala/cats/syntax/bifunctor.scala +++ b/core/src/main/scala/cats/syntax/bifunctor.scala @@ -9,8 +9,7 @@ trait BifunctorSyntax { new BifunctorOps[F, A, B](fab) } -class BifunctorOps[F[_, _], A, B](fab: F[A, B])(implicit F: Bifunctor[F]) { - +final class BifunctorOps[F[_, _], A, B](fab: F[A, B])(implicit F: Bifunctor[F]) { def bimap[C, D](f: A => C, g: B => D): F[C,D] = F.bimap(fab)(f,g) def leftMap[C](f: A => C): F[C, B] = F.leftMap(fab)(f) diff --git a/core/src/main/scala/cats/syntax/compose.scala b/core/src/main/scala/cats/syntax/compose.scala index b55f58d356..6c102ea4d7 100644 --- a/core/src/main/scala/cats/syntax/compose.scala +++ b/core/src/main/scala/cats/syntax/compose.scala @@ -9,7 +9,7 @@ trait ComposeSyntax { new ComposeOps[F, A, B](fab) } -class ComposeOps[F[_, _], A, B](fab: F[A, B])(implicit F: Compose[F]) { +final class ComposeOps[F[_, _], A, B](fab: F[A, B])(implicit F: Compose[F]) { def compose[Z](fza: F[Z, A]): F[Z, B] = F.compose(fab, fza) def andThen[C](fbc: F[B, C]): F[A, C] = F.andThen(fab, fbc) } diff --git a/core/src/main/scala/cats/syntax/either.scala b/core/src/main/scala/cats/syntax/either.scala index bfa98c95a2..a748020604 100644 --- a/core/src/main/scala/cats/syntax/either.scala +++ b/core/src/main/scala/cats/syntax/either.scala @@ -7,6 +7,6 @@ trait EitherSyntax { implicit def eitherSyntax[A, B](eab: Either[A, B]): EitherOps[A, B] = new EitherOps(eab) } -class EitherOps[A, B](val eab: Either[A, B]) extends AnyVal { +final class EitherOps[A, B](val eab: Either[A, B]) extends AnyVal { def toXor: A Xor B = Xor.fromEither(eab) } diff --git a/core/src/main/scala/cats/syntax/eq.scala b/core/src/main/scala/cats/syntax/eq.scala index 09e186c3e9..e3e9faf190 100644 --- a/core/src/main/scala/cats/syntax/eq.scala +++ b/core/src/main/scala/cats/syntax/eq.scala @@ -8,7 +8,7 @@ trait EqSyntax { new EqOps[A](a) } -class EqOps[A: Eq](lhs: A) { +final class EqOps[A: Eq](lhs: A) { def ===(rhs: A): Boolean = macro Ops.binop[A, Boolean] def =!=(rhs: A): Boolean = macro Ops.binop[A, Boolean] } diff --git a/core/src/main/scala/cats/syntax/flatMap.scala b/core/src/main/scala/cats/syntax/flatMap.scala index ba368e1482..ae444f4e15 100644 --- a/core/src/main/scala/cats/syntax/flatMap.scala +++ b/core/src/main/scala/cats/syntax/flatMap.scala @@ -17,7 +17,7 @@ trait FlatMapSyntax extends FlatMapSyntax1 { new IfMOps[F](fa) } -class FlatMapOps[F[_], A](fa: F[A])(implicit F: FlatMap[F]) { +final class FlatMapOps[F[_], A](fa: F[A])(implicit F: FlatMap[F]) { def flatMap[B](f: A => F[B]): F[B] = F.flatMap(fa)(f) def mproduct[B](f: A => F[B]): F[(A, B)] = F.mproduct(fa)(f) def >>=[B](f: A => F[B]): F[B] = F.flatMap(fa)(f) @@ -41,10 +41,10 @@ class FlatMapOps[F[_], A](fa: F[A])(implicit F: FlatMap[F]) { } -class FlattenOps[F[_], A](ffa: F[F[A]])(implicit F: FlatMap[F]) { +final class FlattenOps[F[_], A](ffa: F[F[A]])(implicit F: FlatMap[F]) { def flatten: F[A] = F.flatten(ffa) } -class IfMOps[F[_]](fa: F[Boolean])(implicit F: FlatMap[F]) { +final class IfMOps[F[_]](fa: F[Boolean])(implicit F: FlatMap[F]) { def ifM[B](ifTrue: => F[B], ifFalse: => F[B]): F[B] = F.ifM(fa)(ifTrue, ifFalse) } diff --git a/core/src/main/scala/cats/syntax/foldable.scala b/core/src/main/scala/cats/syntax/foldable.scala index 48d6c9860d..a51cd1055d 100644 --- a/core/src/main/scala/cats/syntax/foldable.scala +++ b/core/src/main/scala/cats/syntax/foldable.scala @@ -14,7 +14,7 @@ trait FoldableSyntax extends Foldable.ToFoldableOps with FoldableSyntax1 { new NestedFoldableOps[F, G, A](fga) } -class NestedFoldableOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Foldable[F]) { +final class NestedFoldableOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Foldable[F]) { def sequence_[B](implicit G: Applicative[G]): G[Unit] = F.sequence_(fga) def foldK(implicit G: MonoidK[G]): G[A] = F.foldK(fga) } diff --git a/core/src/main/scala/cats/syntax/group.scala b/core/src/main/scala/cats/syntax/group.scala index b8790d6798..b2e2ccc9e4 100644 --- a/core/src/main/scala/cats/syntax/group.scala +++ b/core/src/main/scala/cats/syntax/group.scala @@ -9,7 +9,7 @@ trait GroupSyntax extends SemigroupSyntax { new GroupOps[A](a) } -class GroupOps[A: Group](lhs: A) { +final class GroupOps[A: Group](lhs: A) { def |-|(rhs: A): A = macro Ops.binop[A, A] def remove(rhs: A): A = macro Ops.binop[A, A] def inverse(): A = macro Ops.unop[A] diff --git a/core/src/main/scala/cats/syntax/monadCombine.scala b/core/src/main/scala/cats/syntax/monadCombine.scala index 6764c66f00..9d912f7c19 100644 --- a/core/src/main/scala/cats/syntax/monadCombine.scala +++ b/core/src/main/scala/cats/syntax/monadCombine.scala @@ -7,6 +7,6 @@ trait MonadCombineSyntax { new NestedMonadCombineOps[F, G, A](fga) } -class NestedMonadCombineOps[F[_], G[_], A](fga: F[G[A]])(implicit F: MonadCombine[F]) { +final class NestedMonadCombineOps[F[_], G[_], A](fga: F[G[A]])(implicit F: MonadCombine[F]) { def unite(implicit G: Foldable[G]): F[A] = F.unite(fga) } diff --git a/core/src/main/scala/cats/syntax/option.scala b/core/src/main/scala/cats/syntax/option.scala index 3f99168b20..c62f484e7b 100644 --- a/core/src/main/scala/cats/syntax/option.scala +++ b/core/src/main/scala/cats/syntax/option.scala @@ -9,11 +9,11 @@ trait OptionSyntax { implicit def optionSyntax[A](oa: Option[A]): OptionOps[A] = new OptionOps(oa) } -class OptionIdOps[A](val a: A) extends AnyVal { +final class OptionIdOps[A](val a: A) extends AnyVal { def some: Option[A] = Option(a) } -class OptionOps[A](val oa: Option[A]) extends AnyVal { +final class OptionOps[A](val oa: Option[A]) extends AnyVal { def toLeftXor[B](b: => B): A Xor B = oa.fold[A Xor B](Xor.Right(b))(Xor.Left(_)) def toRightXor[B](b: => B): B Xor A = oa.fold[B Xor A](Xor.Left(b))(Xor.Right(_)) def toInvalid[B](b: => B): Validated[A, B] = oa.fold[Validated[A, B]](Validated.Valid(b))(Validated.Invalid(_)) diff --git a/core/src/main/scala/cats/syntax/order.scala b/core/src/main/scala/cats/syntax/order.scala index b251c20bfa..34d5a9055d 100644 --- a/core/src/main/scala/cats/syntax/order.scala +++ b/core/src/main/scala/cats/syntax/order.scala @@ -8,7 +8,7 @@ trait OrderSyntax extends PartialOrderSyntax { new OrderOps[A](a) } -class OrderOps[A: Order](lhs: A) { +final class OrderOps[A: Order](lhs: A) { def compare(rhs: A): Int = macro Ops.binop[A, Int] def min(rhs: A): A = macro Ops.binop[A, A] def max(rhs: A): A = macro Ops.binop[A, A] diff --git a/core/src/main/scala/cats/syntax/partialOrder.scala b/core/src/main/scala/cats/syntax/partialOrder.scala index e133e1966c..3b3dd677a6 100644 --- a/core/src/main/scala/cats/syntax/partialOrder.scala +++ b/core/src/main/scala/cats/syntax/partialOrder.scala @@ -8,7 +8,7 @@ trait PartialOrderSyntax extends EqSyntax { new PartialOrderOps[A](a) } -class PartialOrderOps[A](lhs: A)(implicit A: PartialOrder[A]) { +final class PartialOrderOps[A](lhs: A)(implicit A: PartialOrder[A]) { def >(rhs: A): Boolean = macro Ops.binop[A, Boolean] def >=(rhs: A): Boolean = macro Ops.binop[A, Boolean] def <(rhs: A): Boolean = macro Ops.binop[A, Boolean] diff --git a/core/src/main/scala/cats/syntax/profunctor.scala b/core/src/main/scala/cats/syntax/profunctor.scala index a8b26dc3fa..10ec9eaa39 100644 --- a/core/src/main/scala/cats/syntax/profunctor.scala +++ b/core/src/main/scala/cats/syntax/profunctor.scala @@ -9,7 +9,7 @@ trait ProfunctorSyntax { new ProfunctorOps[F, A, B](fab) } -class ProfunctorOps[F[_, _], A, B](fab: F[A, B])(implicit F: Profunctor[F]) { +final class ProfunctorOps[F[_, _], A, B](fab: F[A, B])(implicit F: Profunctor[F]) { def lmap[C](f: C => A): F[C, B] = F.lmap(fab)(f) def rmap[C](f: B => C): F[A, C] = F.rmap(fab)(f) def dimap[C, D](f: C => A)(g: B => D): F[C, D] = F.dimap(fab)(f)(g) diff --git a/core/src/main/scala/cats/syntax/semigroup.scala b/core/src/main/scala/cats/syntax/semigroup.scala index 3fcf18d4fc..fbfd783f5a 100644 --- a/core/src/main/scala/cats/syntax/semigroup.scala +++ b/core/src/main/scala/cats/syntax/semigroup.scala @@ -9,7 +9,7 @@ trait SemigroupSyntax { new SemigroupOps[A](a) } -class SemigroupOps[A: Semigroup](lhs: A) { +final class SemigroupOps[A: Semigroup](lhs: A) { def |+|(rhs: A): A = macro Ops.binop[A, A] def combine(rhs: A): A = macro Ops.binop[A, A] def combineN(rhs: Int): A = macro Ops.binop[A, A] diff --git a/core/src/main/scala/cats/syntax/split.scala b/core/src/main/scala/cats/syntax/split.scala index 470690502c..a5eaca249e 100644 --- a/core/src/main/scala/cats/syntax/split.scala +++ b/core/src/main/scala/cats/syntax/split.scala @@ -9,6 +9,6 @@ trait SplitSyntax { new SplitOps[F, A, B](fab) } -class SplitOps[F[_, _], A, B](fab: F[A, B])(implicit F: Split[F]) { +final class SplitOps[F[_, _], A, B](fab: F[A, B])(implicit F: Split[F]) { def split[C, D](fcd: F[C, D]): F[(A, C), (B, D)] = F.split(fab, fcd) } diff --git a/core/src/main/scala/cats/syntax/streaming.scala b/core/src/main/scala/cats/syntax/streaming.scala index e17b973615..34180f8774 100644 --- a/core/src/main/scala/cats/syntax/streaming.scala +++ b/core/src/main/scala/cats/syntax/streaming.scala @@ -25,7 +25,7 @@ trait StreamingSyntax { implicit def streamingOps[A](as: => Streaming[A]): StreamingOps[A] = new StreamingOps(Always(as)) - class StreamingOps[A](rhs: Eval[Streaming[A]]) { + final class StreamingOps[A](rhs: Eval[Streaming[A]]) { def %::(lhs: A): Streaming[A] = Cons(lhs, rhs) def %:::(lhs: Streaming[A]): Streaming[A] = lhs ++ rhs } diff --git a/core/src/main/scala/cats/syntax/strong.scala b/core/src/main/scala/cats/syntax/strong.scala index aac6ac2151..b836f6b623 100644 --- a/core/src/main/scala/cats/syntax/strong.scala +++ b/core/src/main/scala/cats/syntax/strong.scala @@ -9,7 +9,7 @@ trait StrongSyntax { new StrongOps[F, A, B](fab) } -class StrongOps[F[_, _], A, B](fab: F[A, B])(implicit F: Strong[F]) { +final class StrongOps[F[_, _], A, B](fab: F[A, B])(implicit F: Strong[F]) { def first[C]: F[(A, C), (B, C)] = F.first(fab) def second[C]: F[(C, A), (C, B)] = F.second(fab) } diff --git a/core/src/main/scala/cats/syntax/traverse.scala b/core/src/main/scala/cats/syntax/traverse.scala index 27cb26eb08..0fb0c866cc 100644 --- a/core/src/main/scala/cats/syntax/traverse.scala +++ b/core/src/main/scala/cats/syntax/traverse.scala @@ -15,7 +15,7 @@ trait TraverseSyntax extends TraverseSyntax1 { new NestedTraverseOps[F, G, A](fga) } -class TraverseOps[F[_], A](fa: F[A])(implicit F: Traverse[F]) { +final class TraverseOps[F[_], A](fa: F[A])(implicit F: Traverse[F]) { def traverse[G[_]: Applicative, B](f: A => G[B]): G[F[B]] = F.traverse(fa)(f) def traverseU[GB](f: A => GB)(implicit U: Unapply[Applicative, GB]): U.M[F[U.A]] = @@ -29,6 +29,6 @@ class TraverseOps[F[_], A](fa: F[A])(implicit F: Traverse[F]) { } -class NestedTraverseOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Traverse[F]) { +final class NestedTraverseOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Traverse[F]) { def sequence(implicit G: Applicative[G]): G[F[A]] = F.sequence(fga) } diff --git a/core/src/main/scala/cats/syntax/validated.scala b/core/src/main/scala/cats/syntax/validated.scala index 629838c517..7d5cbc8fe5 100644 --- a/core/src/main/scala/cats/syntax/validated.scala +++ b/core/src/main/scala/cats/syntax/validated.scala @@ -7,7 +7,7 @@ trait ValidatedSyntax { implicit def validatedIdSyntax[A](a: A): ValidatedIdSyntax[A] = new ValidatedIdSyntax(a) } -class ValidatedIdSyntax[A](val a: A) extends AnyVal { +final class ValidatedIdSyntax[A](val a: A) extends AnyVal { def valid[B]: Validated[B, A] = Validated.Valid(a) def validNel[B]: ValidatedNel[B, A] = Validated.Valid(a) def invalid[B]: Validated[A, B] = Validated.Invalid(a) diff --git a/core/src/main/scala/cats/syntax/xor.scala b/core/src/main/scala/cats/syntax/xor.scala index d9d2006e2c..ca0b4160cf 100644 --- a/core/src/main/scala/cats/syntax/xor.scala +++ b/core/src/main/scala/cats/syntax/xor.scala @@ -7,7 +7,7 @@ trait XorSyntax { implicit def xorIdSyntax[A](a: A): XorIdOps[A] = new XorIdOps(a) } -class XorIdOps[A](val a: A) extends AnyVal { +final class XorIdOps[A](val a: A) extends AnyVal { def left[B]: A Xor B = Xor.Left(a) def right[B]: B Xor A = Xor.Right(a) } diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index cdb87f4690..87a8f20e9f 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -14,7 +14,7 @@ import sbt._ object Boilerplate { import scala.StringContext._ - implicit class BlockHelper(val sc: StringContext) extends AnyVal { + implicit final class BlockHelper(val sc: StringContext) extends AnyVal { def block(args: Any*): String = { val interpolated = sc.standardInterpolator(treatEscapes, args) val rawLines = interpolated split '\n' @@ -41,7 +41,7 @@ object Boilerplate { val maxArity = 22 - class TemplateVals(val arity: Int) { + final class TemplateVals(val arity: Int) { val synTypes = (0 until arity) map (n => s"A$n") val synVals = (0 until arity) map (n => s"a$n") val synTypedVals = (synVals zip synTypes) map { case (v,t) => v + ":" + t} @@ -111,10 +111,10 @@ object Boilerplate { |package cats |package syntax | - |private[syntax] class ApplyBuilder[F[_]] { + |private[syntax] final class ApplyBuilder[F[_]] { | def |@|[A](a: F[A]) = new ApplyBuilder1(a) | - - private[syntax] class ApplyBuilder$arity[${`A..N`}](${params}) { + - private[syntax] final class ApplyBuilder$arity[${`A..N`}](${params}) { - $next - def ap[Z](f: F[(${`A..N`}) => Z])(implicit F: Apply[F]): F[Z] = F.ap$n(${`a..n`})(f) - def map[Z](f: (${`A..N`}) => Z)(implicit F: Apply[F]): F[Z] = F.map$n(${`a..n`})(f) diff --git a/state/src/main/scala/cats/state/StateT.scala b/state/src/main/scala/cats/state/StateT.scala index 7786ea4e23..aa6febbba9 100644 --- a/state/src/main/scala/cats/state/StateT.scala +++ b/state/src/main/scala/cats/state/StateT.scala @@ -123,7 +123,7 @@ private[state] sealed abstract class StateTInstances0 { // To workaround SI-7139 `object State` needs to be defined inside the package object // together with the type alias. -abstract class StateFunctions { +private[state] abstract class StateFunctions { def apply[S, A](f: S => (S, A)): State[S, A] = StateT.applyF(Trampoline.done((s: S) => Trampoline.done(f(s))))