1

I'm trying to write a function that does type casting, which seems to be a frequently occurring activity in Rascal code. But I can't seem to get it right. The following and several variations on it fail.

public &T cast(type[&T] tp, value v) throws str {
    if (tp tv := v)
        return tv;
    else
        throw "cast failed";
}

Can someone help me out?

Some more info: I frequently use pattern matching against a pattern of the form "Type Var" (i.e. against a variable declaration) in order to tell Rascal that an expression has a certain type, e.g.

map[str,value] m := myexp

This is usually in cases where I know that myexp has type map[str,value], but omitting the matching would make Rascal's type checking mechanism complain. In order to be a bit more defensive against mistakes, I usually wrap the matching construct in an if-then-else where an exception is raised if the match fails:

if (map[str,value] m := myexp) {
  // use m
} else {
  throw "cast failed";
}

I would like to shorten all such similar pieces of code using a single function that does the job generically, so that I can write instead

cast(#map[str,value], myexp)

PS. Also see How to cast a value type to Map in Rascal?

Community
  • 1
  • 1
Dennis
  • 171
  • 9
  • Can you post an example of your code? In general, in Rascal you should very rarely wind up with something of type `value`. If you do, you need to use pattern matching to "convert" it into something of the appropriate type, but I don't believe you can do this in such a general way inside Rascal itself, you would probably need to write a function in Java that would perform a conversion based on the reified type you are passing in (what is now `tp`). – Mark Hills May 19 '14 at 12:48
  • Okay, so you can write that directly in Rascal :) As Jurgen mentioned, though, it definitely isn't recommended. – Mark Hills May 20 '14 at 11:57

2 Answers2

1

It seems that the best way to write this, if you truly need to do this, is the following:

public map[str,value] cast(map[str,value] v) = v;
public default map[str,value] cast(value v) { throw "cast failed!"; }

Then you could just say

m = cast(myexp);

and it would do what you want to do -- the actual pattern matching is moved into the function signature for cast, with a case specific to the type you are wanting to use and a case that handles everything that doesn't otherwise match.

However, I'm still not sure why you are using type value, either here (inside the map) or in the linked question. The "standard" Rascal way of handling cases where you could have one of multiple choices is to define these with a user-defined data type and constructors. You could then use pattern matching to match the constructors, or use the is and has keywords to interrogate a value to check to see if it was created using a specific constructor or if it has a specific field, respectively. The rule for fields is that all occurrences of a field in the constructor definitions for a given ADT have the same type. So, it may help to know more about your usage scenario to see if this definition of cast is the best option or if there is a better solution to your problem.

EDITED

If you are reading JSON, an alternate way to do it is to use the JSON grammar and AST that also live in that part of the library (I think the one you are using is more of a stream reader, like our current text readers and writers, but I would need to look at the code more to be sure). You can then do something like this (long output included to give an idea of the results):

rascal>import lang::json::\syntax::JSON;
ok

rascal>import lang::json::ast::JSON;
ok

rascal>import lang::json::ast::Implode;
ok

ascal>js = buildAST(parse(#JSONText, |project://rascal/src/org/rascalmpl/library/lang/json/examples/twitter01.json|));
Value: object((
    "since_id":integer(0),
    "refresh_url":string("?since_id=202744362520678400&q=amsterdam&lang=en"),
    "page":integer(1),
    "since_id_str":string("0"),
    "completed_in":float(0.058),
    "results_per_page":integer(25),
    "next_page":string("?page=2&max_id=202744362520678400&q=amsterdam&lang=en&rpp=25"),
    "max_id_str":string("202744362520678400"),
    "query":string("amsterdam"),
    "max_id":integer(202744362520678400),
    "results":array([
        object((
            "from_user":string("adekamel"),
            "profile_image_url_https":string("https:\\/\\/si0.twimg.com\\/profile_images\\/2206104506\\/339515338_normal.jpg"),
            "in_reply_to_status_id_str":string("202730522013728768"),
            "to_user_id":integer(215350297),
            "from_user_id_str":string("366868475"),
            "geo":null(),
            "in_reply_to_status_id":integer(202730522013728768),
            "profile_image_url":string("http:\\/\\/a0.twimg.com\\/profile_images\\/2206104506\\/339515338_normal.jpg"),
            "to_user_id_str":string("215350297"),
            "from_user_name":string("nurul amalya \\u1d54\\u1d25\\u1d54"),
            "created_at":string("Wed, 16 May 2012 12:56:37 +0000"),
            "id_str":string("202744362520678400"),
            "text":string("@Donnalita122 @NaishahS @fatihahmS @oishiihotchoc @yummy_DDG @zaimar93 @syedames I\'m here at Amsterdam :O"),
            "to_user":string("Donnalita122"),
            "metadata":object(("result_type":string("recent"))),
            "iso_language_code":string("en"),
            "from_user_id":integer(366868475),
            "source":string("<a href="http:\\/\\/blackberry.com\\/twitter" rel="nofollow">Twitter for BlackBerry\\u00ae<\\/a>"),
            "id":integer(202744362520678400),
            "to_user_name":string("Rahmadini Hairuddin")
          )),
        object((
            "from_user":string("kelashby"),
            "profile_image_url_https":string("https:\\/\\/si0.twimg.com\\/profile_images\\/1861086809\\/me_beach_normal.JPG"),
            "to_user_id":integer(0),
            "from_user_id_str":string("291446599"),
            "geo":null(),
            "profile_image_url":string("http:\\/\\/a0.twimg.com\\/profile_images\\/1861086809\\/me_beach_normal.JPG"),
            "to_user_id_str":string("0"),
            "from_user_name":string("Kelly Ashby"),
            "created_at":string("Wed, 16 May 2012 12:56:25 +0000"),
            "id_str":string("202744310872018945"),
            "text":string("45 days til freedom! Cannot wait! After Paris: London, maybe Amsterdam, then southern France, then CANADA!!!!"),
            "to_user":null(),
            "metadata":object(("result_type":string("recent"))),
            "iso_language_code":string("en"),
            "from_user_id":integer(291446599),
            "source":string("<a href="http:\\/\\/mobile.twitter.com" rel="nofollow">Mobile Web<\\/a>"),
            "id":integer(202744310872018945),
            "to_user_name":null()
          )),
        object((
            "from_user":string("johantolsma"),
            "profile_image_url_https":string("https:\\/\\/si0.twimg.com\\/profile_images\\/1961917557\\/image_normal.jpg"),
            "to_user_id":integer(0),
            "from_user_id_str":string("23632499"),
            "geo":null(),
            "profile_image_url":string("http:\\/\\/a0.twimg.com\\/profile_images\\/1961917557\\/image_normal.jpg"),
            "to_user_id_str":string("0"),
            "from_user_name":string("Johan Tolsma"),
            "created_at":string("Wed, 16 May 2012 12:56:16 +0000"),
            "id_str":string("202744274050236416"),
            "text":string("RT @agerolemou: Office space for freelancers in Amsterdam http:\\/\\/t.co\\/6VfHuLeK"),
            "to_user":null(),
            "metadata":object(("result_type":string("recent"))),
            "iso_language_code":string("en"),
            "from_user_id":integer(23632499),
            "source":string("<a href="http:\\/\\/itunes.apple.com\\/us\\/app\\/twitter\\/id409789998?mt=12" rel="nofollow">Twitter for Mac<\\/a>"),
            "id":integer(202744274050236416),
            "to_user_name":null()
          )),
        object((
            "from_user":string("hellosophieg"),
            "profile_image_url_https":string("https:\\/\\/si0.twimg.com\\/profile_images\\/2213055219\\/image_normal.jpg"),
            "to_user_id":integer(0),
            "from_user_id_str":string("41153106"),
            "geo":null(),
            "profile_image_url":string("http:\\/\\/a0.twimg.com\\/profile_images\\/2213055219\\/image_normal.jp...

rascal>js is object;
bool: true

rascal>js.members<0>;
set[str]: {"since_id","refresh_url","page","since_id_str","completed_in","results_per_page","next_page","max_id_str","query","max_id","results"}

rascal>js.members["results_per_page"];
Value: integer(25)

You can then use pattern matching, over the types defined in lang::json::ast::json, to extract the information you need.

Mark Hills
  • 1,028
  • 5
  • 4
  • The solution you suggest seems to be specific for the type map[str,value], which I only used by way of example. I would like something that works for any type. – Dennis May 19 '14 at 19:01
  • As to your question why I'm using `value`: my code reads a file containing a (fairly large) JSON value, using `readTextJSonFile(#map[str,value], inFile)` (from lang::JSON::IO). The structure (i.e. the "type") of the JSON value is not completely known beforehand. If you have any suggestions how to do this "the standard Rascal way" I would be happy to know. Thanks! – Dennis May 19 '14 at 20:00
  • I extended the answer to use the JSON grammar and AST that we have in the Rascal library, hopefully that will work for you. If not, please let me know. – Mark Hills May 20 '14 at 11:59
0

The code has a bug. This is the fixed code:

public &T cast(type[&T] tp, value v) throws str {
    if (&T tv := v)
        return tv;
    else
        throw "cast failed";
}

Note that we do not wish to include this in the standard library. Rather lets collect cases where we need it and find out how to fix it in another way.

If you find you need this casting often, then you might be avoiding the better parts of Rascal, such as pattern based dispatch. See also the answer by Mark Hills.

Jurgen Vinju
  • 6,393
  • 1
  • 15
  • 26