/* CandyBowl ADT Interface, Immutable Method-Chaining Functions
   CSci 555: Functional Programming, Spring 2016
   H. Conrad Cunningham, Professor
   Computer and Information Science
   University of Mississippi

1234567890123456789012345678901234567890123456789012345678901234567890

2016-04-27: Initial version

CandyBowl() to create a new empty candy bowl

put(candyType) to add one piece of candy of type 'candyType' to 
'this' bowl

take(candyType) to remove one piece of candy of type 'candyType'
from 'this' bowl

combine(that) returns the bowl resulting from pouring 'this' bowl
and 'that' bowl together to form a "larger" bowl

isEmpty to determine whether or not 'this' bowl is empty

has(candyType) to determine whether or not 'this' bowl contains any
candy of type 'candyType'

howMany to determine how many pieces of candy are in 'this' bowl
overall

howMany(candyType) to determine how many pieces of candy of type
'candyType' are in 'this' bowl 

inventory to return a list of pairs (candyType, count) for the 
candy in 'this' bowl.  The list should be in ascending order by 
candyType. For example, if candyType is denoted by a string 
and there are two Snickers and one Hershey Kiss in the bowl, 
then the list returned would be something like 
List(("Hershey Kiss", 1), ("Snickers", 2)).

*/

trait CandyBowl[CandyType] {

  def put(candy: CandyType): CandyBowl[CandyType]

  def take(candy: CandyType): Option[CandyBowl[CandyType]]

  def combine(that: CandyBowl[CandyType]): CandyBowl[CandyType]

  def isEmpty: Boolean

  def has(candy: CandyType): Boolean

  def howMany: Int

  def howMany(candy: CandyType): Int

  def inventory: List[(CandyType, Int)]

  def filter(p: CandyType => Boolean): CandyBowl[CandyType]

  def map[B <% Ordered[B]](f: CandyType => B): CandyBowl[B] 

}
