Help constructing scala files

I don’t understand how to contruct a file containing scala code which I can use elsewhere.

  1. How can I define a class in one file which I can instantiate from another file?
  2. How to I tell IntelliJ that I want to compile a particular file? Normally it just knows, but when it doesn’t, it is confusing.

For example. I want to create a class called Graph, and eventually create several applications using that class, Dijkstra search, Bellman-Ford, etc.

Here is the content of the file Graph.scala to start off.

package graph
import scala.collection.immutable

case class Graph[A] (Vertices:Iterable[A],Edges:Iterable[(A,A)]) {
  type Edge = (A,A)
  val edgesGroupedBySource = Edges.groupBy(_._1)
  val edgesGroupedByDest   = Edges.groupBy(_._2)
}

I’ve created that in one IntelliJ window. But IntelliJ doesn’t give me the option to compile or run the content.

Thereafter, I created a Scala worksheet, with the following content.

import graph.Graph._

Graph(List(1,2,3),List((1,2), (1,3), (2,3))).Edges

But when I press the Run icon, I see the following errors.

So I experimented a bit, and I found something that works, but is strange.

I wrapped the class in an object named xyzzy and then I can import that into the worksheet.

Is it the case that I can only reuse classes which I wrap in a dummy object?

Content of Graph.scala

package Graph

import scala.collection.immutable
object xyzzy {

  case class Graph[A](Vertices: List[A], Edges: List[(A, A)]) {
    type Edge = (A, A)
    val edgesGroupedBySource = Edges.groupBy(_._1)
    val edgesGroupedByDest = Edges.groupBy(_._2)

    val Adj: Map[A, List[A]] = edgesGroupedBySource.map { case (src, edges) => (src, edges.map(_._2)) }
  }
}

and in the worksheet

import Graph.xyzzy._

val gbs:Map[Int,List[(Int,Int)]] = List((1,2),(1,3),(2,3)).groupBy(_._1)
def f(src:Int,edges:List[(Int,Int)]):(Int,List[Int]) = {
  (src, edges.map(_._2))
}

Graph(List(1,2,3),List((1,2), (1,3), (2,3))).Edges

You have to have a class with main() or, iirc, App class which are special start points for Java and Scala. Your Graph class will then be instantiable from there and intellij will offer to run the main or App containing class.

If graph.Graph is an object then import graph.Graph._ imports its members. You can’t import members of a class in this way. To import class graph.Graph itself you need just import graph.Graph.

Thanks for the comment. But it’s still not 100% clear to me.

My file Graph.scala looks like the following: I can use import Graph._ in another file Metro.scala, and therein I can instantiate the Graph class. However, I can neither use import Graph._ nor import Graph.Graph in the worksheet. Is that normal?

However if I create a dummy xyzzy object containing the contents of Graph.scala, then I can import into both Metro.scala and also the worksheet.

I’m sure the boiler plating is easy in the end, but I don’t really understand how to do it.

package Graph

import scala.collection.immutable
import scala.Function.const

case class Path[A](g:Graph[A], vertices:List[A],edges:Set[(A,A)]) {
  val numVertices = vertices.size
  ...stuff skipped...
}

case class Graph[A](Vertices: List[A], Edges: List[(A, A)]) {
  val vertexSet = Vertices.toSet
  type Edge = (A, A)
  type Matching = Set[Edge]
  ... stuff skipped ...
}

First of all, using uppercase in package name is confusing, especially when you have class of the same name just inside that package. See naming conventions here: https://docs.scala-lang.org/style/naming-conventions.html

There could be some name clash if you have both package Graph and class Graph in the same scope.

1 Like

In general, I would suggest you find some starter resource on how to use scala, generally those tutorials will also show examples of how to create a runnable application on the Java Virtual Machine with scala code.

If you need to reuse the Graph class inside the same project, you simply need to
import graph.Graph

Yet if you want that to be reusable across projects, you need to first learn how the JVM (which is essentially the java runtime) share code dependencies across projects, hence what is a classpath, how to package library code inside a .jar file (a java library archive), and so on…

In this case a java tutorial might also go a long way to teach you how the JVM works for those things

Ivan, I guess that’s sort of my question huh? The Scala books I’ve read, Okerski (Programming in Scala), Chiusano (Functional Programming in Scala), and now (I’m into) Horstmann (Scala for the Impatient). But they don’t really talk about “What are projects?” are and how to manipulate them.

As per the java tutorial, I’ve been working under the promise that you don’t have to be able to program in Java to program in Scala. However, a resource “All the Java you really need to know to use Scala” would indeed be a nice find.

I understand your point of view, but in fact, while you don’t need to technically know Java - the language - you’re still expected to understand how the java virtual machine and its ecosystem and tooling works.

So you don’t need to know how to write a java application, but still you need resources to understand what to do with your scala jars once you’ve written the code.

I guess Intellij will be able to package your application into an executable jar file.
Otherwise you should learn to use some build tool compatible with scala, sbt being the most popular - and generally considered not necessarily easy to use, after you’re past the basics -
Maven and gradle can be more familiar to people used to jvm programming, but I guess in general you need some basic understanding of how the Jvm, Jre, classpath and resources work in the JVM world.

2 Likes

I would nevertheless like to understand the flow for developing a program, especially using the worksheet. I suspect, now, that part of my problems are simply the limitations of IntelliJ, but my inability to understand when IntelliJ is just wrong. Here’s an example. I refactored the package/object/class as per the suggestions in this thread.

package theg

import scala.collection.immutable
import scala.Function.const
object graph {

  case class Graph[A](Vertices: List[A], Edges: List[(A, A)]) {
    val vertexSet = Vertices.toSet
    type Edge = (A, A)
    type Matching = Set[Edge]

    def isMatching(edges: Matching): Boolean = {
      // This is a function for testing, to make sure machings have been calculated correctly.
      // To test whether the Set of edges is a matching,
      //   we make sure that it is in fact a subset of the graph Edges,
      //   and also it has no duplicates among its src and dst A objects
      val verticesOfEdges: Set[A] = edges.foldLeft(Set[A]()) {
        (acc: Set[A], edge: Edge) => acc.union(Set(getSrc(edge), getDst(edge)))
      }
      edges.subsetOf(Edges.toSet) && 2 * edges.size == verticesOfEdges.size
    }
    ... the rest omitted

The I updated the worksheet as follows:

import Function._
import theg.graph._

def f(src:Int,edges:List[(Int,Int)]):(Int,List[Int]) = {
  (src, edges.map(_._2))
}
val x1 = tupled(f _)
//val y1 = untupled(x1 _)

val g = Graph(List(1, 2, 3, 4), List((3,4), (4,3), (2,1), (3,2), (1, 2), (1, 3), (2, 3), (1,4), (2,4)))
println("" + g.findPath(4, g.testPath,const(false)))

val h = Graph(List(1,2,3,4,5,6,7,8),
              List((1,2),(1,7),
                   (2,3),(2,4),
                   (3,4),(3,5),(3,7),
                   (4,8),
                   (5,6),(5,8),
                   (6,7),(6,8)))
h.findMaximumMatching(Set())

When I tried to evaluate the worksheet, I got an error (sorry I don’t remember the exact message) to the effect that it is unable to evaluate graph in theg.graph._. Sigh :sleepy: so after I closed it up for the day, deciding have a fresh look tomorrow. The next day, I git-pulled my code from my office computer, and IT WORKS PERFECTLY. I think there’s something in the IntelliJ environment which is confused, because I’m not managing any of the IntelliJ bookkeeping files in git.

Moral of the story, there are lots and lots of magical things that need to work together, and it’s pretty difficult to understand. And when it doesn’t work, distinguishing tool issues, from novice mistakes is also not so easy.

1 Like

I think it’s absolutely important that you share your experience here, so that we become more aware of what hurdles are there for people learning from the basics.
Possibly something good will come out of this, and the learning path will be easier and easier with time.

1 Like

With IntelliJ, I have found that it works best using sbt integration as opposed to a standalone Scala project. If you combine that with ensuring “Auto Import” is enabled to pick up any changes, things tend to work reasonably well. Occasionally, I have had to force IntelliJ to rebuild the project (menu option via right-click) in order for changes to local classes to become available via other files.

It’s probably worth noting: I find IntelliJ worksheets incomprehensible personally – the few times I’ve tried to use them, I’ve failed to get anything done, so I just ignore the feature. For simpler stuff, I do it in Scalafiddle; for more complex, I usually create scratchpad Scalatest tests…

BTW, there’s a current discussion of possibly moving this sort of thing into the language proper in Scala 3, which might hopefully make this work better and more consistently in the future – make it actually part of Scala, not just part of the IntelliJ IDE.

1 Like

Hello,

A project is a folder that contains sufficient files to build something. What exactly that entails depends on your build tool, such as SBT. IntelliJ has a Scala plugin that makes it understand SBT projects.

If you want to make contents of one project available in another project, there are different options:

  • Some build tools allow you to specify that one project depends on another. In SBT, this is possible among sub-projects of the same parent project.

  • Otherwise, you can build a project into a JAR file and manually include that JAR file in another project

  • The preferred way: you can publish your project as an artifact locally or to a remote artifact repository. Artifacts are identified by a unique triple of group id, artifact id and version. If you simply tell SBT to publish local, you can use your artifact from another project on the same machine. You are supposed to change the version every time you publish a modified project, but if it’s just for your own use, who cares.

1 Like