6
votes

I have a function that looks like this:

package org.thimblr.io
import java.io._
object Local {
  def streamer(path: String) = () => new FileReader(path)
}

This manages basically what I want to do, which is to return a function that opens a stream from a file when it's called. So client code can do this:

val planStreamSource = Local.streamer("/home/someuser/.plan")
//...passes the function on to somewhere else
val planStream = planStreamSource()
val firstByte = planStream.read
//can now read from planStream

But what I'd really like is to return a lazy val that streams from a file once it's referenced, like this:

val planStream = Local.streamer("/home/someuser/.plan")
//...passes the val on to somewhere else, without opening the file for reading yet
val firstByte=planStream.read
//...such that planStream was only just opened to allow the read

Is it possible to do something like this, return a lazy val, so that client code can treat it as a value rather than a function?

1
Why don't you just do def streamer(path: String) = new FileReader(path) and lazy val planStream = Local.streamer("/home/someuser/.plan")? - Alexey Romanov
because i want to supply this val to somewhere else in the code, interchangeably with a stream that's coming not from a file, but from a socket or something else. If I pass planStream in as an argument to another function, I don't want the file to be opened yet at that point. I want it to be opened when the client code is actually ready to start using the stream (inside a try finally block). - traffichazard
Trying to treat Scala as if it were a lazily evaluated language is going badly against its grain. You can use lazy vals and by-name parameters to create deferred-evaluation value containers, but I can't recommended as a system-wide style of programming. - Randall Schulz

1 Answers

15
votes

You can't “return a lazy val” — client code must declare it as lazy. If you don't want to force the client to declare a lazy val, you could return a wrapper instead:

class LazyWrapper[T](wrp: => T) {
  lazy val wrapped: T = wrp
}

object LazyWrapper {
  implicit def unboxLazy[T](wrapper: LazyWrapper[T]): T = wrapper.wrapped
}

And then:

def streamer(path: String) = new LazyWrapper(new FileReader(path))

You could further forward equals, hashCode, etc. to the wrapped object in LazyWrapper if you need those.