2

I have a resource object stored in an option.

 private var ochan: Option[Channel] = None

At some point during program execution, ochan is set to Some(channel). I'd like to close the channel (via invoking the method close) and set the option to None in one fatal swoop.

Currently I have:

 def disconnect = ochan = { ochan.foreach{_.close}; None }

And previously I had:

 def disconnect = ochan = ochan.flatMap{ o => o.close; None }

Is there a better way to do this?

Andy
  • 5,108
  • 3
  • 26
  • 37

6 Answers6

5

I'd write it like this:

def disconnect = ochan = ochan match {
  case Some(ch) => ch.close(); None
  case None => None // do nothing
}

instead of using foreach or flatMap. In my opinion, this solution shows more clearly and explicitly what happens. The solution with foreach or flatMap requires an extra mental jump, you'd have to know what these methods do on an Option.

Jesper
  • 202,709
  • 46
  • 318
  • 350
3

I don't know that it's better but it's shorter (once you've defined the implicit):

implicit def closer(o: Option[Channel]) = new { 
  def close(): Option[Channel] = { o.foreach(_.close); None } 
}

def disconnect = ochan = ochan.close
huynhjl
  • 41,520
  • 14
  • 105
  • 158
  • Writing a second function which duplicates the first function in order to simplify the first function seems more complex to me ... and less readable. – Fred Haslam Apr 08 '11 at 18:14
  • @Anonymous If that's an issue then you simply add synchronization. I don't think this is an issue in practice, you'd most likely not use the same channel object from multiple threads at the same time. – Jesper Apr 11 '11 at 09:47
1

There is no big difference between an immutable var and a mutable val. So why not encapsulate the behavior in a separate class, when you want to have mutability anyway?

class ChannelOption {
  private var _channel :Option[Channel] = None
  def channel = _channel
  def channel_=(ch:Option[Channel]) { _channel.foreach(_.close); _channel = ch }
}

Usage:

private val ochan = new ChannelOption
ochan.channel = Some(getAChannel)
ochan.channel.foreach(useChannel)
ochan.channel = Some(getAnotherChannel) //this automatically closes the first channel
ochan.channel = None //this automatically closes the second channel
Landei
  • 54,104
  • 13
  • 100
  • 195
1

It's not thread safe! Remember to use @volatile (not here; using synchronization), and do something like this: (this is why I don't like imperative code)

private val lock = new Object

def disconnect() {//Function has side effects: remember parenthesis!
  lock.synchronized { //Synchronizations is important; you don't want to close it multiple times
    ochan.foreach {_.close()} //Again: side effects -> parens.
  }
}

And if you don't use parallel programming, you are doing something wrong.

Anonymous
  • 821
  • 1
  • 5
  • 14
0

You could define ochan_= so that assigning a new value to ochan closes the old channel (similar to std::auto_ptr<> in C++) but I don't see how you can encapsulate that in a child class of Option[Channel] because the storage is in your class. The solution wouldn't change the code much at all, it would just make disconnect implicit by assigning ochan.

Ben Jackson
  • 90,079
  • 9
  • 98
  • 150
0

I guess this could work:

def disconnect { 
  ochan = {
    ochan.get.close
    None
  }
}

or

def disconnect {
  ochan.get.close
  ochan = None
}

Anyway since there is mutating operation, it will always need 2 calls (1 for close and one for assignment of None).

Antonin Brettsnajdr
  • 4,073
  • 2
  • 20
  • 14
  • Don't use the `get` method, it's not typesafe. – Jesper Nordenberg Apr 07 '11 at 21:01
  • 1
    @Jesper Nordenberg I think it's still "typesafe", perhaps "not None-safe"? (`val x: Option[String] = None; x.get` -- compiles fine, runs not-so-much) –  Apr 07 '11 at 21:10
  • `get` will throw a runtime exception when called on `None`, making it compile time type unsafe. – Jesper Nordenberg Apr 07 '11 at 21:16
  • @Jesper Nordenberg See above. It is still [typesafe](http://en.wikipedia.org/wiki/Type_safety). `null.asInstanceOf[String].length` is arguably typesafe (although perhaps I am pushing the boundary here with `asInstanceOf`? the previous comment was well in the bounds of typesafety as I understand it, though) –  Apr 07 '11 at 21:22
  • @Jesper Nordenberg Consider `val someOption = getUnknownOption(); someOption.get` as being similar to `val y: Int = getUnknown(); val z = 1 / y` (where whether `someOption` is Some/None or `y` is 0/non-0 is not known) –  Apr 07 '11 at 21:29
  • I'm not familiar with the exact definition of "typesafe" that you are using, but to quote the text you linked to _In the context of static (compile-time) type systems, type safety usually involves (among other things) a guarantee that the eventual value of any expression will be a legitimate member of that expression's static type._. A runtime exception is certainly not a member of the type Option[T], so I will continue to call `get` type unsafe (for a lack of a better term). – Jesper Nordenberg Apr 08 '11 at 07:35