Recent posts (max 20) - Browse or Archive for more

How the Functional Ability of Scala Comparing to Haskell - an Example

About one and half year ago, it was my first time to consider Scala seriously, I wrote a blog about the syntax example of Scala, Erlang and Haskell .

With more experience of Scala, I'd like to know how about the functional ability of Scala comparing to Haskell. I picked up  Paul R. Brown's perpubplat blog engine again, which is a Haskell implementation heavily in functional style. I tried to port more code from Haskell to Scala keeping in similar expressions. Here's the code example of Entry.scala in Scala comparing to Brown's original Entry.hs:

Original Haskell code piece

-- | Data structures for an item (post or comment) and the
-- overall structure in terms of parents and children.
module Blog.Model.Entry where

import qualified Blog.FrontEnd.Urls as U
import Utilities
import qualified Blog.Constants as C

import Maybe
import List ( sortBy, isPrefixOf, intersperse)
import qualified Data.Map as M
import Data.Map ( (!) )

type ISO8601DatetimeString = String
type XhtmlString = String

-- | Overall data model for the runtime.  
data Model = Model { -- | 
                     by_permatitle :: M.Map String Item,
                     by_int_id :: M.Map Int Item,
                     child_map :: M.Map Int [Int],
                     all_items :: [Item],
                     next_id :: Int }

empty :: Model
empty = Model M.empty M.empty M.empty [] 0

data Kind = Post | Comment | Trackback
            deriving (Show, Read, Eq)

build_model :: [Item] -> Model
build_model [] = empty
build_model items = Model (map_by permatitle sorted_items)
                    bid                    
                    (build_child_map sorted_items)
                    (sorted_items)
                    (n+1)
    where
      sorted_items = sort_by_created_reverse items
      bid = (map_by internal_id sorted_items)
      n = fst . M.findMax $ bid

build_child_map :: [Item] -> M.Map Int [Int]
build_child_map i = build_child_map_ (M.fromList $ (map (\x -> (internal_id x,[])) i)) i

-- Constructed to take advantage of the input being in sorted order.
build_child_map_ :: M.Map Int [Int] -> [Item] -> M.Map Int [Int]
build_child_map_ m [] = m
build_child_map_ m (i:is) = if (parent i == Nothing) then
                                build_child_map_ m is
                            else
                                build_child_map_ (M.insertWith (++) (unwrap $ parent i) [internal_id i] m) is

-- | Insert an item, presuming that all of its data other than
-- internal identifier have been correctly set.
insert :: Model -> Item -> (Item,Model)
insert m i = (i', m { by_permatitle = M.insert (permatitle i') i' $ by_permatitle m
                    , by_int_id = M.insert n i' $ by_int_id m
                    , child_map = M.insert (internal_id i') [] $
                                  case parent i of
                                    Nothing ->
                                        child_map m
                                    (Just p_id) ->
                                        M.insert p_id (insert_comment_ m (item_by_id m p_id) i') $ child_map m
                    , all_items = insert_ after (all_items m) i'
                    , next_id = n + 1 } )
    where
      n = next_id m
      i' = i { internal_id = n }

insert_comment_ :: Model -> Item -> Item -> [Int]
insert_comment_ m p c = map internal_id (insert_ before (children m p) c)

insert_ :: (Item -> Item -> Bool) -> [Item] -> Item -> [Item]
insert_ _ [] y = [y]
insert_ o s@(x:xs) y = if (x `o` y) then
                           (x:(insert_ o xs y))
                       else
                           (y:s)

after :: Item -> Item -> Bool
after a b = (created a) > (created b)

before :: Item -> Item -> Bool
before a b = (created a) < (created b)

-- | Apply a structure-preserving function, i.e., one that does not
-- change parent/child relationships or ids, to a specific item.
alter :: (Item -> Item) -> Model -> Item -> IO Model
alter f m i = do { ts <- now
                 ; let i' = (f i) { updated = ts }
                 ; return $ m { by_permatitle = M.insert (permatitle i') i' $ by_permatitle m
                              , by_int_id = M.insert (internal_id i') i' $ by_int_id m
                              , child_map = if (parent i == Nothing) then
                                                child_map m
                                            else
                                                M.insert p_id resort_siblings $ child_map m
                              , all_items = insert_ after all_but i' } }
    where
      not_i = \item -> (internal_id item) /= (internal_id i)
      all_but = filter not_i $ all_items m
      p_id = unwrap $ parent i
      p = item_by_id m p_id
      resort_siblings = map internal_id (insert_ before (filter not_i $ children m p) i)

cloak :: Model -> Item -> IO Model
cloak = alter (\i -> i { visible = False })

uncloak :: Model -> Item -> IO Model
uncloak = alter (\i -> i { visible = True })

permatitle_exists :: Model -> String -> Bool
permatitle_exists = (flip M.member) . by_permatitle

max_id :: Model -> Int
max_id = fst . M.findMax . by_int_id

post_by_permatitle :: Model -> String -> Item
post_by_permatitle = (!) . by_permatitle

maybe_post_by_permatitle :: Model -> String -> Maybe Item
maybe_post_by_permatitle = (flip M.lookup) . by_permatitle

item_by_id :: Model -> Int -> Item
item_by_id = (!) . by_int_id

children :: Model -> Item -> [Item]
children m i = map (item_by_id m) ((child_map m) ! (internal_id i))

unwrap :: Maybe a -> a
unwrap (Just x) = x
unwrap Nothing = error "Can't unwrap nothing!"

data Author = Author { name :: String,
                       uri :: Maybe String,
                       email :: Maybe String,
                       show_email :: Bool
                     }
              deriving ( Show,Read,Eq )

-- | General purpose runtime data structure for holding a post or
-- comment.  For a comment, a number of the fields will be ignored
-- (e.g., comments and tags) until/if the presentation and syndication
-- system gets fancier.
data Item = Item { -- | an internal unique number for this post
                   internal_id :: Int,
                   -- | the kind of item that this represents
                   kind :: Kind,
                   -- | the title of the post, as it should be rendered on
                   -- the web or inserted in an Atom feed; this should be a
                   -- valid XHTML fragment.
                   title :: XhtmlString,
                   -- | the summary of the post, as it should be rendered on
                   -- the web or intersted into an Atom feed; this should be
                   -- a valid XHTML fragment.
                   summary :: Maybe XhtmlString,
                   -- | the body of the post as an XHTML fragment.  This
                   -- will be wrapped in an XHTML @<div>@ when rendered on
                   -- the web or in a feed.
                   body :: XhtmlString,
                   -- | tags for the post, if any, expected to be in
                   -- alphabetical order and consisting of letters, digits,
                   -- dashes, and/or underscores.
                   tags :: [String],
                   -- | a generated UID for the post; this is expected to be
                   -- suitable for use as an Atom GUID.  The expectation is
                   -- that it will be supplied by the implementation when
                   -- the post is ingested.
                   uid :: String,
                   -- | a permanent title for the item, consisting of only
                   -- lowercase letters, digits, and dashes.
                   permatitle :: String,
                   -- | the timestamp, as an ISO8601 datetime, when the post
                   -- came into being.  This is never blank and would be
                   -- supplied by the implementation when the post is
                   -- ingested.
                   created :: ISO8601DatetimeString,
                   -- | the timestamp, as an ISO8601 datetime, when the post
                   -- was updated.  Initially, this is equal to the value of
                   -- the 'created' field.
                   updated :: ISO8601DatetimeString,
                   -- | the author of the post, expected to be hardwired to
                   -- the author of the blog
                   author :: Author,
                   -- | whether or not the item is to be displayed.
                   visible :: Bool,
                   -- | this item's parent, if any.
                   parent :: Maybe Int
                 }
            deriving ( Show, Read, Eq )

-- | Compute a permalink for the item relative to the supplied base URL.
permalink :: Model
          -> Item -- ^ the item
          -> String
permalink m i = U.post (relative_url m i)

relative_url :: Model -> Item -> String
relative_url m = _form_permalink . (ancestors m)

_form_permalink :: [Item] -> String
_form_permalink [] = ""
_form_permalink [i] =  let s = permatitle i in 
                       if (kind i == Post) then
                           "/" ++ s
                       else
                           "#" ++ s
_form_permalink (i:is) = if (kind i == Post) then
                            ("/" ++ permatitle i) ++ (_form_permalink is)
                        else 
                            (_form_permalink is)


ancestor_path :: Model -> Item -> String
ancestor_path m i = concat . (intersperse "/") . (map permatitle) $ ancestors m i

ancestors :: Model -> Item -> [Item]
ancestors m i = ancestors_ m [] (Just $ internal_id i)

ancestors_ :: Model -> [Item] -> Maybe Int -> [Item]
ancestors_ _ is Nothing = is
ancestors_ m is (Just i) = ancestors_ m (i':is) (parent i')
    where
      i' = item_by_id m i

lastUpdated :: [Item] -> ISO8601DatetimeString
lastUpdated ps = maximum (map updated ps)

drop_invisible :: [Item] -> [Item]
drop_invisible = filter visible

sort_by_created :: [Item] -> [Item]
sort_by_created = sortBy created_sort

created_sort :: Item -> Item -> Ordering
created_sort a b = compare (created a) (created b)

sort_by_created_reverse :: [Item] -> [Item]
sort_by_created_reverse = sortBy created_sort_reverse

created_sort_reverse :: Item -> Item -> Ordering
created_sort_reverse a b = compare (created b) (created a)

-- | Filter a list of items according to a date fragment
date_fragment_filter_ :: ISO8601DatetimeString -> [Item] -> [Item]
date_fragment_filter_ s = filter ((s `isPrefixOf`) . created)

-- | Filter a list of posts for those made in a specific year.
year_filter :: Int -- ^ year
            -> [Item] -> [Item]
year_filter y = date_fragment_filter_ $ show y

-- | Filter a list of posts for those made in a specific month.              
month_filter :: Int -- ^ year
             -> Int -- ^ month
             -> [Item] -> [Item]
month_filter y m | (0 < m) && (m < 13) = date_fragment_filter_ ((show y) ++ (pad_ m))
                 | otherwise = const []

-- | Filter a list of posts for those made on a specific day
day_filter :: Int -- ^ year
           -> Int -- ^ month
           -> Int -- ^ day
           -> [Item] -> [Item]
day_filter y m d = date_fragment_filter_ ((show y) ++ (pad_ m) ++ (pad_ d))

-- | Utility function to zero pad months and days in date expressions.
pad_ :: Int -> String
pad_ i | i < 10 = "-0" ++ (show i)
       | otherwise = ('-':(show i))
              
-- to do: make this faster using the sortedness.
tags_filter :: [String] -> [Item] -> [Item]
tags_filter t p = foldl (flip ($)) p (map tag_filter t)

tag_filter :: String -> [Item] -> [Item]
tag_filter t = filter ((t `elem`) . tags) 

plink_filterf :: String -> Item -> Bool
plink_filterf = flip $ (==) . permatitle

plink_filter :: String -> [Item] -> [Item]
plink_filter = filter . plink_filterf

ymd_plink_finder :: Int -> Int -> Int -> String -> [Item] -> [Item]
ymd_plink_finder y m d t = (plink_filter t) . (day_filter y m d)

all_posts :: Model -> [Item]
all_posts = (filter (\x -> Post == kind x)) . all_items

all_comments :: Model -> [Item]
all_comments = (filter (\x -> Comment == kind x)) . all_items

flatten :: Model -> [Item] -> [Item]
flatten m = flatten_ (children m)

flatten_ :: (a -> [a]) -> [a] -> [a]
flatten_ _ [] = []
flatten_ f (i:is) = (i:(flatten_ f (f i))) ++ (flatten_ f is)

concat_comments :: Model -> [Item] -> [Item]
concat_comments m = (foldr (++) []) . (map $ children m)

(</>) :: String -> String -> String
s </> t = s ++ ('/':t)

to_string :: Item -> String
to_string i = concat [metadata i, "\n", body_block i, "\n", summary_block i]

metadata :: Item -> String
metadata i = unlines $ apply i [ ("internal_id",show . internal_id),
                                 ("parent", show . parent),
                                 ("title",title),
                                 ("tags",show_no_quotes . tags),
                                 ("permatitle",permatitle),
                                 ("kind",show . kind),
                                 ("uid",uid),
                                 ("created",created),
                                 ("updated",updated),
                                 ("author",show . author),
                                 ("visible",show . visible) ]

show_no_quotes :: [String] -> String
show_no_quotes = concat . (intersperse ", ")

apply :: Item -> [(String,(Item -> String))] -> [String]
apply _ [] = []
apply i (x:xs) = ((concat [fst x, ": ", (snd x) i]) : (apply i xs))

body_block :: Item -> String
body_block i = concat ["--- START BODY ---\n",
                       (body i),
                       "\n--- END BODY ---\n"]

summary_block :: Item -> String
summary_block i | summary i == Nothing = ""
                | otherwise = concat ["--- START SUMMARY ---\n",
                                      (unwrap $ summary i),
                                      "\n--- END SUMMARY ---\n"]

default_author :: Author
default_author = Author C.author_name C.author_uri C.author_email True

In Scala:

  • Formatted and highlighted by NetBeans Scala Plugin, exported via [File] -> [Print to HTML ...]
package org.aiotrade.blog.model

import java.util.Calendar
import org.aiotrade.blog.{Constants => C}

object Entry {
  type XhtmlString = String

  class Model (
    var by_permatitle: Map[String, Item],
    var by_int_id: Map[Int, Item],
    var child_map: Map[Int, List[Int]],
    var all_items: List[Item],
    var next_id: Int
  ) {
    // call by name
    def apply(block: => Unit) = {block; this}
  }

  abstract class Kind //deriving (Show, Read, Eq)
  case object Post extends Kind
  case object Comment extends Kind
  case object Trackback extends Kind

  case class Author (
    var name: String,
    var uri : Option[String] = None,
    var email: Option[String] = None,
    var show_email: Boolean = false
  )

  /** General purpose runtime data structure for holding a post or
   * comment.  For a comment, a number of the fields will be ignored
   * (e.g., comments and tags) until/if the presentation and syndication
   * system gets fancier.
   */
  case class Item (
    // an internal unique number for this post
    var internalId: Int,
    // the kind of item that this represents
    var kind: Kind,
    // the title of the post, as it should be rendered on
    // the web or inserted in an Atom feed; this should be a
    // valid XHTML fragment.
    var title: XhtmlString,
    // the summary of the post, as it should be rendered on
    // the web or intersted into an Atom feed; this should be
    // a valid XHTML fragment.
    var summary: Option[XhtmlString],
    // the body of the post as an XHTML fragment.  This
    // will be wrapped in an XHTML @<div>@ when rendered on
    // the web or in a feed.
    var body: XhtmlString,
    // tags for the post, if any, expected to be in
    // alphabetical order and consisting of letters, digits,
    // dashes, and/or underscores.
    var tags: List[String],
    // a generated UID for the post; this is expected to be
    // suitable for use as an Atom GUID.  The expectation is
    // that it will be supplied by the implementation when
    // the post is ingested.
    var uid: String,
    // a permanent title for the item, consisting of only
    // lowercase letters, digits, and dashes.
    var permatitle: String,
    // the timestamp, as an ISO8601 datetime, when the post
    // came into being.  This is never blank and would be
    // supplied by the implementation when the post is
    // ingested.
    var created: Long,
    // the timestamp, as an ISO8601 datetime, when the post
    // was updated.  Initially, this is equal to the value of
    // the 'created' field.
    var updated: Long,
    //the author of the post, expected to be hardwired to
    // the author of the blog
    var author: Author,
    //whether or not the item is to be displayed.
    var visible: Boolean,
    //this item's parent, if any.
    var parent: Option[Int]
  ) {
    def apply(block: Item => Unit) = {block(this); this}
  }

  def empty = new Model(Map(), Map(), Map(), Nil, 0)

  def build_model(is: List[Item]) =
    is match {
      case Nil => empty
      case _ =>
        val sortedIs = sort_by_created_reverse(is)
        val bid = Map() ++ sortedIs.map{x => (x.internalId -> x)}
        val n = bid.keySet.max

        new Model(Map() ++ sortedIs.map{x => (x.permatitle -> x)},
                  bid,
                  buildChildMap(sortedIs),
                  sortedIs,
                  n + 1)
    }

  def buildChildMap(is: List[Item]) =
    buildChildMap_(Map() ++ is.map(_.internalId -> Nil), is)

  def buildChildMap_(map: Map[Int, List[Int]], is: List[Item]) =
    map ++ {
      for (i <- is if i.parent.isDefined) yield {
        // pid, cids definitions go into body // it's more efficient.
        val pid = i.parent.get
        val cids = map.getOrElse(pid, Nil)
        pid -> (i.internalId :: cids)
      }
    }

  /** Insert an item, presuming that all of its data other than
   internal identifier have been correctly set.
   */
  def insert(m: Model, i: Item): (Item, Model) = {
    val n = m.next_id
    i.internalId = n

    (i, m {
        m.by_permatitle += (i.permatitle -> i)
        m.by_int_id += (n -> i)
        m.child_map = (i.parent match {
            case None => m.child_map
            case Some(p_id) => m.child_map + (p_id -> (insert_comment_(m, item_by_id(m)(p_id), i)))
          })
        m.all_items = insert_(after, m.all_items, i)
        m.next_id = n + 1 }
    )
  }

  def insert_comment_(m: Model, p: Item, c: Item): List[Int] = 
    insert_(before, children(m)(p), c) map (_.internalId)

  def insert_(o: (Item, Item) => Boolean, is: List[Item], y: Item): List[Item] =
    is match {
      case Nil => List(y)
      case x :: xs => if (o(x, y)) x :: insert_(o, xs, y) else (y :: is)
    }
  
  def after (a: Item, b: Item): Boolean =
    a.created > b.created

  def before(a: Item, b: Item): Boolean =
    a.created < b.created

  /**
   * Apply a structure-preserving function, i.e., one that does not
   * change parent/child relationships or ids, to a specific item.
   */
  def alter(f: (Item => Item), m: Model, i: Item): Model = {// -> IO Model
    val not_i = (item: Item) => item.internalId != i.internalId
    val all_but = m.all_items filter not_i
    val p_id = unwrap (i.parent)
    val p = item_by_id(m)(p_id)
    val resort_siblings = insert_(before, children(m)(p) filter not_i, i) map (_.internalId)

    val ts = System.currentTimeMillis
    val i1 = f(i) {_.updated = ts}
    m {
      m.by_permatitle += (i1.permatitle -> i1)
      m.by_int_id += (i1.internalId -> i1)
      m.child_map = i.parent match {
        case None => m.child_map
        case _ => m.child_map + (p_id -> resort_siblings)
      }
      m.all_items = insert_(after, all_but, i1)
    }
  }

  def cloak(m: Model, i: Item): Model = // -> IO Model
    alter (i => i {_.visible = false}, m, i)

  def uncloak(m: Model, i: Item): Model = // -> IO Model
    alter (i => i {_.visible = true}, m, i)

  def permatitle_exists(m: Model, p: String): Boolean =
    m.by_permatitle.contains(p)

  def max_id(m: Model): Int =
    m.by_int_id.keySet.max

  def post_by_permatitle(m: Model, p: String): Item =
    m.by_permatitle(p)

  def maybe_post_by_permatitle(m: Model, p: String): Option[Item] =
    m.by_permatitle.get(p)

  def item_by_id(m: Model)(id: Int): Item = 
    m.by_int_id(id)

  def children(m: Model)(i: Item): List[Item] =
    m.child_map(i.internalId) map (item_by_id(m))

  def unwrap[T](a: Option[T]): T =
    a match {
      case Some(x) => x
      case None => error("Can't unwrap none!")
    }

  def relative_url(m: Model, i: Item): String =
    _form_permalink(ancestors(m, i))

  def _form_permalink(is: List[Item]): String =
    is match {
      case Nil => ""
      case i :: Nil =>
        val s = i.permatitle
        if (i.kind == Post)
          "/" + s
        else
          "#" + s
      case i :: is =>
        if (i.kind == Post)
          ("/" + i.permatitle) + _form_permalink(is)
        else
          _form_permalink(is)
    }

  def ancestors(m: Model, i: Item): List[Item] =
    ancestors_(m, Nil, Some(i.internalId))

  def ancestors_(m: Model, is: List[Item], i_? : Option[Int]): List[Item] =
    i_? match {
      case None => is
      case Some(i) =>
        val i1 = item_by_id(m)(i)
        ancestors_(m, i1 :: is, i1.parent)
    }

  def lastUpdated(ps: List[Item]): Long =
    ps map (_.updated) max

  def drop_invisible(is: List[Item]): List[Item] =
    is filter (_.visible)

  def sort_by_created(is: List[Item]): List[Item] =
    is sortWith created_sort _

  def created_sort(a: Item, b: Item) =
    a.created < b.created

  def sort_by_created_reverse(is: List[Item]): List[Item] =
    is sortWith created_sort_reverse _

  def created_sort_reverse(a: Item, b: Item) =
    b.created < a.created

  def date_fragment_filter_(is: List[Item], ts: Int*) = {
    val cal = Calendar.getInstance
    ts match {
      case Seq(y, m, d) => is filter {i => pad_(cal, i.created) match {
            case (`y`, `m`, `d`) => true
            case _ => false
          }
        }
      case Seq(y, m) => is filter {i => pad_(cal, i.created) match {
            case (`y`, `m`, _) => true
            case _ => false
          }
        }
      case Seq(y) => is filter {i => pad_(cal, i.created) match {
            case (`y`, _, _) => true
            case _ => false
          }
        }
    }
  }
  
  def year_filter(y: Int)(is: List[Item]): List[Item] =
    date_fragment_filter_(is, y)

  def month_filter(y: Int, m: Int)(is: List[Item]): List[Item] =
    date_fragment_filter_(is, y, m)

  def day_filter(y: Int, m: Int, d: Int)(is: List[Item]): List[Item] =
    date_fragment_filter_(is, y, m, d)

  def pad_(cal: Calendar, t: Long): (Int, Int, Int) = {
    cal.setTimeInMillis(t)
    import Calendar._
    (cal.get(YEAR), cal.get(MONTH) + 1, cal.get(DAY_OF_MONTH))
  }

  def tags_filter(ts: List[String])(is: List[Item]): List[Item] =
    is filter (i => (false /: ts) {_ || i.tags.contains(_)})

  def tag_filter(t: String)(is: List[Item]): List[Item] =
    is filter (_.tags.contains(t))

  def plink_filterf(p: String)(i: Item): Boolean =
    i.permatitle == p

  def plink_filter(p: String)(is: List[Item]): List[Item] =
    is filter plink_filterf(p)

  def ymd_plink_finder(y: Int, m: Int, d: Int, p: String)(is: List[Item]): List[Item] =
    plink_filter(p) (day_filter(y, m, d) (is))

  def </> (s: String, t: String): String =
    s + '/' + t

  def all_posts(m: Model): List[Item] =
    m.all_items filter (_.kind == Post)

  def all_comments(m: Model): List[Item] =
    m.all_items filter (_.kind == Comment)

  def flatten(m: Model, is: List[Item]): List[Item] =
    flatten_(children(m), is)

  def flatten_[T](f: T => List[T], is: List[T]): List[T] =
    is match {
      case Nil => Nil
      case i :: is => i :: flatten_(f, f(i)) ::: flatten_(f, is)
    }

  def concat_comments(m: Model, is: List[Item]): List[Item] =
    ((is map children(m)) :\ List[Item]())(_ ::: _)

  def metadata(i: Item): String =
    apply(i, List(("internal_id", show(_.internalId)),
                  ("parent",      show(_.parent)),
                  ("title",       _.title),
                  ("tags",        show_no_quotes(_.tags)),
                  ("permatitle",  _.permatitle),
                  ("kind",        show(_.kind)),
                  ("uid",         _.uid),
                  ("created",     show(_.created)),
                  ("updated",     show(_.updated)),
                  ("author",      show(_.author)),
                  ("visible",     show(_.visible)))
    ) mkString "\n"

  // curring
  def show(f: Item => Any)(i: Item): String =
    f(i) toString

  def show_no_quotes(f: Item => List[String])(i: Item): String =
    f(i) mkString ", "

  def apply(i: Item, xs: List[(String, Item => String)]): List[String] =
    xs match {
      case Nil => Nil
      case x :: xs => (x._1 + ": " + x._2(i)) :: apply(i, xs)
    }

  def body_block(i: Item): String =
    "--- START BODY ---\n" + i.body + "\n--- END BODY ---\n"

  def summary_block(i: Item): String =
    i.summary match {
      case None => ""
      case Some(x) => "--- START SUMMARY ---\n" + x + "\n--- END SUMMARY ---\n"
    }

  val default_author = Author(C.author_name, C.author_uri, C.author_email, true)
}

Progress of Migrating AIOTrade to Scala #2

My next step is to push AIOTrade to another level of UE, with hotkey for symbols, indicators etc, with better UI.

So far, my Scala adventure went almost smoothly, I'm thinking about the data exchange between client and data source, maybe the actors from  Scala,  LiftWeb or  AKKA will bring some fresh atmosphere.

AIOTrade-100125a.png

AIOTrade-100125b.png

Progress of Migrating AIOTrade to Scala

Well, I've done most parts of migrating AIOTrade to Scala, not all features return yet. I gain lots of experiences of inter-op between Scala and Java, since AIOTrade has to be integrated into an existed Java framework  NetBeans Platform. And also, whole project is now managed by Maven instead of Ant, which reduces lots of pain of dependencies upon crossing sub-projects.

This project is now hosted on  kenai.com  http://sf.net/projects/humaitrader, you can check out the code to get an overview of how to integrated Maven + Scala + NetBeans Modules. Of course, all were done with NetBeans Scala plugin.

LOC of this project so far:

$ ./cloc.pl --read-lang-def=lang_defs.txt ~/myprjs/aiotrade.kn/opensource/
     677 text files.
     617 unique files.                                          
     154 files ignored.

http://cloc.sourceforge.net v 1.08  T=3.0 s (167.7 files/s, 21373.7 lines/s)
-------------------------------------------------------------------------------
Language          files     blank   comment      code    scale   3rd gen. equiv
-------------------------------------------------------------------------------
Scala               353      7981     16301     27180 x   1.36 =       36964.80
Java                 43      1148       833      6946 x   1.36 =        9446.56
XML                 104       231       389      2414 x   1.90 =        4586.60
Bourne Shell          2        81        81       488 x   3.81 =        1859.28
HTML                  1         7        15        26 x   1.90 =          49.40
-------------------------------------------------------------------------------
SUM:                503      9448     17619     37054 x   1.43 =       52906.64
-------------------------------------------------------------------------------

A screen snapshot:

AIOTrade-100117.png

Jetty + FastCGI Servlet + Trac = blogtrader.net

I ported  jfastcgi to Scala and fixed some bugs. Now my web site is configured running under Jetty with the modified fastcgi servlet as gateway to a Trac fastcgi daemon.

I encountered an issue (fixed later) with Trac fcgi daemon though, which may cause the daemon died silently, but the fastcgi servlet will restart it automatically when this happens.

All source code can be found via Browse Source

  • Posted: 2010-01-03 02:00 (Updated: 2010-01-03 21:36)
  • Author: dcaoyuan
  • Categories: (none)
  • Comments (0)

Importing Old Blogs Almost Done

I've almost done importing old blogs from Roller db to this Trac based one, except image/resource links.

The pretty url of this web site is also working now.

New Web Site for blogtrader.net

I just re-launched blogtrader.net, which is now based on trac, but on glassfish V3.0 (moved to Jetty 7.0 later) via a custom CGIServlet. It's a headache to get a pretty url for trac, I may re-explore it later.

All old blogs should be imported back here, but not now.

  • Posted: 2009-12-28 10:10 (Updated: 2009-12-28 23:29)
  • Author: dcaoyuan
  • Categories: (none)
  • Comments (0)

About Caoyuan's Blog

Caoyuan's blogs about future.

For more information about this site see Wiki

  • Posted: 2009-12-28 03:19 (Updated: 2010-01-17 10:25)
  • Author: dcaoyuan
  • Categories: (none)
  • Comments (0)

How to Setup Dependencies Aware Maven Project for Scala

I have to say, maintain a couple of dependent projects via Ant is a headache, just like me and others may have encountered when use NetBeans' Scala plugin to create and maintain plain Scala projects, these projects, are all Ant based. It's difficult to write a generic Ant template to compute the dependencies graph and then choose the best building path. The building process of cross dependent projects becomes very slow because the redundant enter/exit dependent projects building task.

NetBeans has also the best Maven plugin integrated, I decided to give Maven a trying. This was actually my first attempt to create Maven based project(s). I explored the mini way toward a Scala Maven project, and patched Scalac a bit again to get the dependencies aware compiling working.

Now here's an example I'd like to share with you.

Assume we have n sub-projects, with cross dependencies, for instance, 'lib.util' and 'lib.math', 'lib.indicators' etc. The simplest Maven way is to keep a parent project (I call it 'modules' here) which holds all common settings and module(sub-project) names(paths). The directory structure could be like:

modules
|-- pom.xml
|-- lib.util
|   |-- pom.xml
|   `-- src
|       |-- main
|       |   `-- scala
|       |       `-- lib
|       |           `-- util
|       |               `-- App.scala
|       `-- test
|           `-- scala
|               `-- lib
|                   `-- util
|                       `-- AppTest.scala
|-- lib.math
    |-- pom.xml
    `-- src
        |-- main
        |   `-- scala
        |       `-- lib
        |           `-- math
        |               `-- App.scala
        `-- test
            `-- scala
                `-- lib
                    `-- math
                        `-- AppTest.scala

What I like Maven here is that the parent project 'modules' only maintains the common settings and module paths, all dependencies between sub-projects are then set in sub-project's self pom.xml file. This is a good decoupled strategy in my eyes, you can freely combine these sub-projects at any time without change too much, and each sub-project doesn't need to care about the dependencies of other projects. The parent project is only a centre place for sharing common setting and a centre place to list all available projects and their directories, it's just like a Directory Service.

Now, here is the pom.xml of parent project looks like (all common settings include compiler, repository etc are kept in it):

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>org.aiotrade</groupId>
    <artifactId>modules</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <name>AIOTrade Modules</name>
    <description>Basic Modules for AIOTrade.</description>

    <properties>
        <scala.version>2.8.0-SNAPSHOT</scala.version>
    </properties>

    <repositories>
        <repository>
            <id>scala-tools.release</id>
            <name>Scala-Tools Release Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </repository>
        <repository>
            <id>scala-tools.snapshot</id>
            <name>Scala-Tools Snapshot Repository</name>
            <url>http://scala-tools.org/repo-snapshots</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>scala-tools.org</id>
            <name>Scala-Tools Maven2 Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </pluginRepository>
    </pluginRepositories>

    <dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.specs</groupId>
            <artifactId>specs</artifactId>
            <version>1.2.5</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>process-resources</phase> <!-- to support mix java/scala only -->
                        <goals>
                            <goal>add-source</goal>      <!-- to support mix java/scala only -->
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <scalaVersion>${scala.version}</scalaVersion>
                    <args>
                        <arg>-target:jvm-1.5</arg>
                        <arg>-make:transitivenocp</arg>
                        <arg>-dependencyfile</arg>
                        <arg>${project.build.directory}/.scala_dependencies</arg>
                    </args>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <reporting>
        <plugins>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <configuration>
                    <scalaVersion>${scala.version}</scalaVersion>
                </configuration>
            </plugin>
        </plugins>
    </reporting>
    
    <modules>
        <module>lib.math</module>
        <module>lib.util</module>
        <module>lib.indicator</module>
    </modules>
</project>

And the pom.xml of sub-project 'lib.util' looks like:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.aiotrade</groupId>
        <artifactId>modules</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>lib-util</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>lib-util</name>

</project>

The 'lib.math' one:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.aiotrade</groupId>
        <artifactId>modules</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>lib-math</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>lib-math</name>
    
    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>lib.util</artifactId>
            <version>${project.version}</version>
            <type>jar</type>
        </dependency>
    </dependencies>

</project>

That's all the settings, clear and clean.

Now, let's back to another level of dependencies: dependencies between Scala source files. How does this setting get scalac aware dependencies of changed sources?

Again, just like the scalac setting in an Ant based project, where at 'configuration' part of build/plugins/plugin/@maven-scala-plugin, I add "-make:transitivenocp -dependencyfile ${project.build.directory}/.scala_dependencies" to 'args' (should be put in separate 'arg' tag as following)

                <configuration>
                    <scalaVersion>${scala.version}</scalaVersion>
                    <args>
                        <arg>-target:jvm-1.5</arg>
                        <arg>-make:transitivenocp</arg>
                        <arg>-dependencyfile</arg>
                        <arg>${project.build.directory}/.scala_dependencies</arg>
                    </args>
                </configuration>

Now the last question, how to use NetBeans to create above setting?

Open "File" | "New Project..." | "Maven" | "Maven Project", click "Next", choose "Maven Quickstart Archetype" to create each empty project, then copy/paste above content to corresponding pom.xml, change project groupId, artifactId and version properties. For existed project, copy your source code to "main/scala". You can also mixed Java code, put then at "main/java", NetBeans Scala plugin supports mixed Scala/Java project well.

I'll put the pre-defined archetype on net later, so no need to copy/paste, or, you can create an empty parent/sub-project, commit to your version control system. BTW, I find manually modify pom.xml is a pleasure work.

  • Note No. 1: You have to use/update-to Scala-2.8.0-SNAPHOST with at least scala-compiler-2.8.0-20091128-xxx.jar
  • Note No. 2: If your project is simple, just use NetBeans created Ant based project. If you separate it to a couple of dependent projects, I strongly suggest you to Maven.
  • Note No. 3: A new Scala plugin for NetBeans 6.8RC1 will be available before or at Monday, it's also a must upgrade especially for Windows users.

How to Setup Dependencies Aware Ant Project for Scala

During the past days, I was patching scalac ant task and some relative issues, and now, the dependencies aware ant scalac works (post Scala 2.8.0.r19724).

Below is an example build.xml with dependencies aware setting:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project name="ScalaAntTest" default="build" basedir=".">
 3     <description>Builds, tests, and runs the project ScalaAntTest.</description>
 4 
 5     <property name="src.dir" value="${basedir}/src"/>
 6     <property name="build.dir" value="${basedir}/build"/>
 7     <property name="build.classes.dir" value="${build.dir}/classes"/>
 8 
 9     <target name="init">
10         <property environment="env"/>
11         <condition property="scala.home" value="${env.SCALA_HOME}">
12             <isset property="env.SCALA_HOME"/>
13         </condition>
14         <fail unless="scala.home">set SCALA_HOME first</fail>
15 
16         <property name="scala-library.jar" value="${scala.home}/lib/scala-library.jar"/>
17         <property name="scala-compiler.jar" value="${scala.home}/lib/scala-compiler.jar"/>
18 
19         <path id="build.classpath">
20             <pathelement location="${scala-library.jar}"/>
21             <pathelement location="${scala-compiler.jar}"/>
22             <pathelement location="${build.classes.dir}"/>
23         </path>
24         <taskdef resource="scala/tools/ant/antlib.xml">
25             <classpath>
26                 <pathelement location="${scala-compiler.jar}"/>
27                 <pathelement location="${scala-library.jar}"/>
28             </classpath>
29         </taskdef>
30     </target>
31 
32     <target name="build" depends="init">
33         <mkdir dir="${build.dir}"/>
34         <mkdir dir="${build.classes.dir}"/>
35         <scalac srcdir="${src.dir}"
36                 destdir="${build.classes.dir}"
37                 classpathref="build.classpath"
38                 force="yes"
39                 addparams="-make:transitive -dependencyfile ${build.dir}/.scala_dependencies"
40                 >
41             <src path="${basedir}/src1"/> 
42             <!--include name="compile/**/*.scala"/-->
43             <!--exclude name="forget/**/*.scala"/-->
44         </scalac>
45     </target>
46 
47     <target name="clean" depends="init">
48         <delete dir="${build.dir}"/>
49     </target>
50 
51 </project>

There are some tips here, I'll give a concise explanation:

First, there will be a file call ".scala_dependencies" which is put under "build/" directory after you first clean-build, it will record all dependencies information. Since it's put under "build/", it will be removed automatically after an "ant clean". The "-dependencyfile ${build.dir}/.scala_dependencies" parameter of scalac at line 39 enables this.

Second, you should add "-make:transitive" as scalac's parameter (line 39), which will enable scalac to evaluate the dependencies transitive.

Third, add attribute "force='yes'" (line 38), which tells scalac to check all source files for dependencies and re-compile them if files that dependents on changed.

Forth, you should include "<pathelement location='${build.dir.classes}'>" as part of "build.classpath" (line 22), so scalac won't complain lack of already generated classes when rebuild upon parts of source files.

I've also re-write the project scheme that created by NetBeans plugin, that is, the next released NetBeans Scala plugin will automatically generate dependencies aware build.xml file for new created projects. Unfortunately, you'll have to copy/move your old project src to new created project directory if you want to benefit from it.

For some reasons, "fsc" is no longer supported as the default compiler task in NetBeans created project, which actually brought some annoyances for plugin users. Even without "fsc", the dependencies aware "scalac" should also work satisfiable in most cases.

  • Posted: 2009-11-19 08:00 (Updated: 2009-12-30 04:39)
  • Author: dcaoyuan
  • Categories: Java
  • Comments (0)

Scala Plugin for NetBeans - Available for NetBeans 6.8beta and Scala 2.8.0 Snapshot

I packed another release of Scala plugin for NetBeans 6.8 beta, for more information, please see:

 http://wiki.netbeans.org/Scala68v1

This version allows adding "deprecation", "unchecked" parameters for scalac, and fixed unicode id issue. It works on Scala-2.8.0 snapshot only.

Note:

All projects created via "File" -> "New Project" before this version should add or change following lines in "nbproject/project.properties":

scalac.compilerargs=
scalac.deprecation=no
scalac.unchecked=no

Scala Plugin for NetBeans - Available for NetBeans 6.8m2 and Scala 2.8.0 Snapshot

As NetBeans 6.8 m2 released, I packed a downloadable binary which works with Scala 2.8.0.r18993 or above.

Please do not install Scala plugin via Update Center for NetBeans 6.8 m2, it's not compatible. Use the link below to download and install

New & Noteworthy

  • Much more responsive when typing and for code-completion
  • More refactoring: find usages, rename across opened projects
  • Breakpoint works everywhere (almost)
  • Better supporting for mixed Java/Scala project in both direction (Java is visible in Scala and vice versa)
  • Better integration with NetBeans maven plugin
  • Better code completion even for implicit methods (in most cases)
  • Better indentation and formatter for unfinished line after 'if', 'else', '=', 'for', 'while', 'do' etc
  • Better syntax highlighting for val/var, lazy val, implicit call, byname param, abstract method etc
  • Mark of implemented/overridden methods, click on mark will jump to super definition
  • Go to type ("Ctrl+O")
  • Select parts of code, press '{', '[', '(', '"', '`' will add/replace surrounding pair, press '~' will remove them. Specially, press '/' will block-comment it
  • Reset Scala interactive parser when necessary, for instance: dependent libs changed (Right click on source, choose "Reset Scala Parser" in pop-up menu)
  • Output highlighted code to html ([File] -> [Print to HTML...])
  • Some basic hints, for instance: fixing import, unused imports
  • Code template now works, check or add your own via [Options/Preferences] -> [Editor] -> [Code Templates] -> [Scala]

Install with NetBeans 6.8 M2

  1. Download and install the latest Scala-2.8.0 snapshot runtime via Scala's home
  2. Set $SCALA_HOME environment variable to point to the installed Scala runtime path. Add $SCALA_HOME/bin to PATH environment variable. Note for Mac OS user, $SCALA_HOME environment variable may not be visible for Applications/NetBeans, see  http://wiki.netbeans.org/MacOSXEnvForApp
  3. Get the NetBeans 6.8 M2 from:  http://bits.netbeans.org/netbeans/6.8/m2/
  4. Get the Scala plugins binary from:  https://sourceforge.net/projects/erlybird/files/nb-scala/6.8v1.1.0m2/nb-scala-6.8v1.1.0m2.zip/download
  5. Unzip Scala plugin binary to somewhere
  6. Open NetBeans, go to "Tools" -> "Plugins", click on "Downloaded" tab title, click on "Add Plugins..." button, choose the directory where the Scala plugins are unzipped, select all listed *.nbm files, following the instructions.

Tips

  • If you encounter "... Could not connect to compilation daemon.", try to run "fsc -reset" under a command/terminal window.
  • Editor $NetBeansInstallationPath?/etc/netbeans.conf, remove "-ea" to avoid AssertionError? popped up

 http://wiki.netbeans.org/Scala68v1 for more information

Notice for Host Migration of This Site

I'm going to migrate this web site to new host during this month, both blogtrader.net and aiotrade.com may not be available at any time during this period.

Scala Plugin for NetBeans - Rewrite in Scala #8: Partly Visible to Java and Go to Type

>>> Updated on Sep 8:

Now in most cases, Scala is visible to Java, including generic type. Mixed Java/Scala project should be well supported.

======

I need "Go To Type" working, and Scala's symbols should be visible to Java sources. For NetBeans, that means I should implement a Scala to Java VirtualSourceProvider?, which will translate Scala source to Java stub.

I tried it before, but not so successful, so I disabled it. Finally, I got how it works today, and committed work in part. That is, now, in Scala/Java mixed project, not only Java is visible to Scala source, and also, partly, Scala is visible to Java too.

Another benefit is, when you press "Ctrl + O" or "Command + O", a navigator window will bring you to Type's source file that you are searching.

I'll go on to get Scala -> Java mapping fully works soon.

>>> Updated on Sep 8:

The following issue was fixed in trunk.

======

Warning: If you have not got a NetBeans nightly built before, do not try it until a recent (after Sep 3rd) serious fault fixed.

ScalaEditor-090905.png

Scala Plugin for NetBeans - Rewrite in Scala #7: Mark Override Method and Go to Super def

The new progress is a little mark at the left side bar of editor, showing if a method is overriding a method of base class/trait. Move cursor on this mark will show a tooltip, and, click on this mark will jump to source of super definition.

If a mark of (D) shown and you forget to add "override", you will wait until a whole build process to tell you lacking of "override", so pay attention to it.

Of course, if it's an (I) mark, you do not need to add "override".

ScalaEditor-090904.png

Scala Plugin for NetBeans - Rewrite in Scala #5: Refactoring Step One - Finding usages

I'm working on Refactoring features for NetBeans' Scala plugin, the first step is finding usages of class/method/val etc across project's source files, it works now. The next step will be renaming them.

Following snapshot shows class "ScalaDfn?" are used in 19 places, across 6 Scala sources

ScalaEditor-090902.png

Scala Plugin for NetBeans - Rewrite in Scala #6: Refactoring Step Two - Rename

As I've done most code of Refactoring skeleton, it did not cost me too much time to get renaming feature working (2 hours about). Now, NetBeans' Scala plugin can rename class/method/val etc across project's source files.

Following snapshot shows class "Dog" is going to be renamed to "BigDog?". After the preview, press "Do Refactoring", all things done.

ScalaEditor-090903.png

Scala Plugin for NetBeans - Rewrite in Scala #4: How to Use It to Develop Scala Itself

I begin to patch Scala's new designed compiler interface for IDE to get it work better (with NetBeans, and, other IDEs). I know lamp term may use Eclipse as their IDE to develop Scala itself, but I'm using new written NetBeans plugin. Here's a short tutorial of how to use NetBeans Scala plugin to work with Scala itself

Build Scala-2.8.0 snapshot

First and least, svn check out Scala's trunk source code:

cd ~/myprjs/scala/scala
svn co http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk

Then, make sure your $JAVA_HOME is pointed to a JDK 1.5 instead of 1.6, and <b>clear $SCALA_HOME</b>. "ant dist" to build a fresh Scala distribution, which is located at ~myprjs/scala/scala/dist/latest

# As for Mac OS X
JAVA_HOME_BAK=$JAVA_HOME
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home
export SCALA_HOME=
ant dist
export JAVA_HOME=$JAVA_HOME_BAK
export SCALA_HOME=~myprjs/scala/scala/dist/latest

Install latest NetBeans and Scala plugin

Download latest NetBeans nightly built from  http://bits.netbeans.org/download/trunk/nightly/latest, the minimal pack "Java SE" is enough.

Run NetBeans, get latest Scala plugin via: [Preference/Option] -> [Plugins] -> [Available Plugins], find "Scala Kit" in the list and choose it,following the instructions to get it installed, restart NetBeans.

Create NetBeans project by importing existing sources

Create project for Scala's trunk sources. <b>Note: each folder under "src" should be considered as standalone folder, for example, "compiler"</b>, it's better to create standalone project for each these folder, I created two, one for "compiler",another for "library".

[File] -> [New Project] -> [Scala] -> [Scala Project with Existing Sources]

ScalaTrunkProject1.png

Click [Next], input project name that you like Note: you can input an existed ant build script file name in "Build Script Name". Or, modify the auto-created one later, it's a plain ant build xml file.

ScalaTrunkProject2.png

Click [Next], add source folder:

ScalaTrunkProject2.png

Click [Next] again, you can see the sources that will be included in this project, then click [Finish]

ScalaTrunkProject4.png

Now, Scala's compiler has been imported as a NetBeans freeform project, it is an old plain ant project. You can create another one for Scala's library.

ScalaTrunkProject5.png

How to debug into Scala's compiler, library source?

If you have a project that want to debug into Scala's compiler, library sources, you could do when in debugging:

In debugging, open debugging source windows via: [Windows] -> [Debugging] -> [Sources]. Go through the listed source jars, find ".../scala-compiler.jar" and ".../scala-library.jar", check them.

Scala Plugin for NetBeans - Rewrite in Scala #3: Ready for Beta Test

I struggled with  new redesigned IDE / compiler interface (interative.Global) during this weekend, and finally, got it attached to NetBeans Scala plugin, which runs as a background daemon thread, thus the plugin is more responsive when coding (the first benefit). It was a hard work though, I had to modify some of the original code to get whole things stable and responsive (balance), it paid me 2 sleepless nights.

So here's what's new on current nightly built:

  • Tested and work with Scala 2.8.0.r18542 snapshot
  • Better supporting for mixed Java/Scala project
  • Better indentation and formatter for unfinished line after 'if', 'else', '=', 'for', 'while', 'do' etc
  • Better code completion even for implicit methods (some times)
  • Implicit method call will be highlighted by underline
  • Select parts of code, press '{', '[', '(', '"', '`' etc will add/replace surrounding pair, press '~' will remove them. Specially, press '/' will block comment it
  • Some basic hints, for example, fix import (from Milos' work)
  • Code template now works, check or add your own via [Options/Preferences] -> [Editor] -> [Code Templates] -> [Scala] (from Milos' work)

Note: wait until Monday night for NetBeans' hudson builds those whole new nbms.

I think this plugin is qualifiable for beta using/testing now. If you are interested in testing/reporting bugs, you can get the latest NetBeans nightly built, and got the new Scala plugin from Update Center, download a Scala-2.8.0 snapshot, live your $SCALA_HOME to this Scala 2.8.0 snapshot, change $PATH to including $SCALA_HOME/bin.

By testing/using Scala 2.8.0 right now, we can also push it improved more quickly.

BTW, I'm using this plugin on whole Scala' trunk source and of course, this plugin.

Scala Plugin for NetBeans - Rewrite in Scala #2: Supports Java/Scala Mixed Project

Java/Scala mixed project was supported partly once before, but I cannot remember when this feature got lost. Anyway, as one progressing of the rewritten NetBeans Scala plugin, I just got this feature working properly halfway: now Java source is visible to Scala one, but not vice versa. To got Scala source also visible for Java source, would pay for efficiency, since the compiler may need to be pushed to a deep phase, and mapped to Java classes/methods, I need more time to think about that.

Below is a screenshot that a JavaDog? is called from a Scala source file:

ScalaEditor-090810.png

Scala Plugin for NetBeans - Rewrite in Scala #1: Almost Done

The previous Scala plugin for NetBeans was a rush work which was written in Java, toward a useful tool to support writing Scala code. I called it chicken, which was then can be used to produce new Scala plugin written in Scala, that was, the egg.

The egg has grown to a chicken now. As for last night, I switched whole scala modules to depend on the new written scala.editor module (in Scala), and use this new chicken to improve Scala plugin from now on.

The new scala plugin will be carefully rewritten, with a lot of APIs supporting for language editor in mind, and try to support better and more features toward a whole featured, stable Scala IDE.

Another good news is, Milos Kleint, the contributor to NetBeans Maven plugin, has also put hands on this project, he's working on better Scala support for maven project, and new templates/hinds features for Scala editor modules.

The new plugin is based on Scala 2.8 snapshot, if want to get it, you should use the nightly built NetBeans from netbeans.org, and get the new plugins via updated center.

Note: you have to install the latest Scala 2.8 snapshot too, and make sure $SCALA_HOME, $PATH pointed to it. For maven project, you should also update your pom.xml to depend on Scala -2.8-snapshot.

Below is a working snapshot that I was using new Scala plugin to write Scala plugin:

ScalaEditor-090808.png