First String.type is not doing what you think it does. Second, the type keyword just creates a type-alias, as such, it is just assigning a new name to the type on the right-hand side of the equals, it is not really defining a new type.
Thus, it can not be recursive or have cycles since then it doesnβt know to what to expand.
In this case, I believe the best is to just define new classes.
final case class Environment(data: List[(String, Formula)]) // Maybe a map would be better?
final case class Formula(run: Environment => Either[String, Formula])