0

I am just starting to learn Scala and was following this tutorial. They implement a Tree structure as the code below shows to create mathematical formula's. Halfway through they introduce the type keyword as

type Environment = String => Int

so that all variables can be mapped to numbers.

My question is, how do I refer to the type without having an instance of Tree? I.e. how can I define the type Environment as static.


Example code:

package com.company

/**
  * The tree class which represents a formula
  */
abstract class Tree {
    /**
      * The type to map a variable to a value
      */
    type Environment = String => Int

    /**
      * Using the given Environment, it evaluates this tree
      * @param env The environment which maps variables to values
      * @return The result of the fomula
      */
    def eval(env: Environment): Int = this match {
        case Sum(lhs, rhs) => lhs.eval(env) + rhs.eval(env)
        case Variable(v)   => env(v)
        case Constant(c)   => c
    }
}

/**
  * Represents a sum between to values
  * @param lhs Left hand value
  * @param rhs Right hand value
  */
case class Sum(lhs: Tree, rhs: Tree) extends Tree

/**
  * Represents an unknown and named value or named variable
  * @param variable The name of the variable
  */
case class Variable(variable: String) extends Tree

/**
  * An unnamed constant value
  * @param value The value of this constant
  */
case class Constant(value: Int) extends Tree

/**
  * Base class for this program
  */
object Tree {
    /**
      * Entry point of the application
      * @param args
      */
    def main(args: Array[String]) {
        //Define a tree: (x + 3) + 2
        val tree: Tree = Sum(Sum(Variable("x"), Constant(3)), Constant(2))
        //Define an environment: x=5 and y=6
        val env: tree.Environment = {case "x" => 5; case "y" => 6}
        //       ^ Refers to the object tree rather than the class Tree
        //val env: Tree.Environment = {case "x" => 5; case "y" => 6}
        //  ^ Results in the error: Error:(55, 27) type Environment is not a member of object com.company.Tree

        println(tree) //show the tree
        println(s"x=${env.apply("x")} | y=${env.apply("y")}") //show values of x and y
        println(tree.eval(env)) //evaluates the tree
    }
}
Didii
  • 1,194
  • 12
  • 36

2 Answers2

1

Use # (type projection) to access type members without referring to the instance:

 val env: Tree#Environment = {case "x" => 5; case "y" => 6}

More explanation provided here: What does the `#` operator mean in Scala?

P.S. You cannot actually make your type "static" in the full sense of the word - aka static member, as JVM actually "forgets" about such types (erasure), so both tree.Environment and Tree.Environment (surprisingly) are processed by compiler, not in runtime. Even object Tree{ type Environment = ... } means that your type is going to be a member of singleton object Tree (so Tree would still have a reference)

dk14
  • 22,206
  • 4
  • 51
  • 88
  • Why couldn't the `.`-operator suffice to refer to `Environment`? Because `val Tree: Tree` is possible I presume? – Didii Jun 15 '17 at 14:46
  • @Didii it's a syntax thing. `.` is meant to access members from some reference (singletons). `#` doesn't require such. See [Type Projection](https://www.scala-lang.org/files/archive/spec/2.11/03-types.html) – dk14 Jun 15 '17 at 14:49
  • @Didii `val Tree: Tree` actually wouldn't be a big problem for a language - except it would just hurt readability and esthetic then. The reason is that `val Tree` could just "shadow" `class Tree` (like local value could shadow some other value from outer scope) - it's a common practice in Scala (sometimes good, sometimes not). – dk14 Jun 15 '17 at 15:07
  • 1
    @dk14: there is no "shadowing" going on. Values and Types live in two different namespaces, there can never be any confusion. (Otherwise companion objects would make no sense.) – Jörg W Mittag Jun 15 '17 at 16:58
  • @JörgWMittag of course it's not! I was referring to hypothetic situation described by the OP: `val Tree: Tree` and then `Tree.Env` - if (IF!!! it's not what's really happening) `.` was used for type projection compiler would have to decide what to do: use `Env` type from instance `Tree` or class `Tree` - so in that purely theoretical case compiler could shadow class by instance. Again, it's not what happening as there is a separate operator for type projection `#`, just saying that it wouldn't be a problem (but wouldn't be pretty as well) to implement it that way. I hope it's clear now. – dk14 Jun 15 '17 at 21:22
1

In Scala types, just like everything else, can be made "static" by placing them inside an object.

abstract class Tree
object Tree {
  type Environment = String => Int
}

I say "static", because Scala (the language) has no notion of static like some other programming languages.

Jasper-M
  • 14,966
  • 2
  • 26
  • 37