NetBeans(6.8) plugin for Scala 2.8.0 RC1
I'm please to announce the NetBeans (6.8) plugin for Scala 2.8.0 RC1 is available now. It's a maintain release to catch up with Scala 2.8.0 RC1.
The packed plugins is downloadable at: http://sourceforge.net/projects/erlybird/files/nb-scala/6.8v1.1.0rc2/
It requires Scala 2.8.0 RC1, which is available at: http://www.scala-lang.org/downloads. For maven project, it's under http://www.scala-tools.org/repo-releases, with version number "2.8.0.RC1"
Please refer to http://wiki.netbeans.org/Scala68v1 for the installation/upgrading information.
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.
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:
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
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.
About Caoyuan's Blog
Caoyuan's blogs about future.
For more information about this site see Wiki
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> <jvmArgs> <jvmArg>-Xms64m</jvmArg> <jvmArg>-Xmx1024m</jvmArg> </jvmArgs> <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> <jvmArgs> <jvmArg>-Xms64m</jvmArg> <jvmArg>-Xmx1024m</jvmArg> </jvmArgs> <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.
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
- Download and install the latest Scala-2.8.0 snapshot runtime via Scala's home
- 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
- Get the NetBeans 6.8 M2 from: http://bits.netbeans.org/netbeans/6.8/m2/
- 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
- Unzip Scala plugin binary to somewhere
- 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.
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".
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.
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]
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.
Click [Next], add source folder:
Click [Next] again, you can see the sources that will be included in this project, then click [Finish]
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.
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: