I have an application which is written entirely using the FRP paradigm and I think I am having performance issues due to the way that I am creating the streams. It is written in Haxe but the problem is not language specific.
For example, I have this function which returns a stream that resolves every time a config file is updated for that specific section like the following:
function getConfigSection(section:String) : Stream<Map<String, String>> {
return configFileUpdated()
.then(filterForSectionChanged(section))
.then(readFile)
.then(parseYaml);
}
In the reactive programming library I am using called promhx each step of the chain should remember its last resolved value but I think every time I call this function I am recreating the stream and reprocessing each step. This is a problem with the way I am using it rather than the library.
Since this function is called everywhere parsing the YAML every time it is needed is killing the performance and is taking up over 50% of the CPU time according to profiling.
As a fix I have done something like the following using a Map stored as an instance variable that caches the streams:
function getConfigSection(section:String) : Stream<Map<String, String>> {
var cachedStream = this._streamCache.get(section);
if (cachedStream != null) {
return cachedStream;
}
var stream = configFileUpdated()
.filter(sectionFilter(section))
.then(readFile)
.then(parseYaml);
this._streamCache.set(section, stream);
return stream;
}
This might be a good solution to the problem but it doesn't feel right to me. I am wondering if anyone can think of a cleaner solution that maybe uses a more functional approach (closures etc.) or even an extension I can add to the stream like a cache function.
Another way I could do it is to create the streams before hand and store them in fields that can be accessed by consumers. I don't like this approach because I don't want to make a field for every config section, I like being able to call a function with a specific section and get a stream back.
I'd love any ideas that could give me a fresh perspective!
Well, I think one answer is to just abstract away the caching like so:
class Test {
static function main() {
var sideeffects = 0;
var cached = memoize(function (x) return x + sideeffects++);
cached(1);
trace(sideeffects);//1
cached(1);
trace(sideeffects);//1
cached(3);
trace(sideeffects);//2
cached(3);
trace(sideeffects);//2
}
#:generic static function memoize<In, Out>(f:In->Out):In->Out {
var m = new Map<In, Out>();
return
function (input:In)
return switch m[input] {
case null: m[input] = f(input);
case output: output;
}
}
}
You may be able to find a more "functional" implementation for memoize down the road. But the important thing is that it is a separate thing now and you can use it at will.
You may choose to memoize(parseYaml) so that toggling two states in the file actually becomes very cheap after both have been parsed once. You can also tweak memoize to manage the cache size according to whatever strategy proves the most valuable.
These 4 lines compile but do not make sense to me :
open System
type mclas (y) =
member x.m = x.m
let z = mclas (1:>obj)
Question : In what case would we need to code in such way ? Am I activating subtle class features I am not aware of ?
Edit : If there is no use case, what is the status of this piece of code regarding compiler warning/error and is it eligible for some Issue raising on github ?
Note : At runtime, the debugger cannot evaluate variable z saying "Function evaluation timed out".
The member m actually compiles to something like this in IL:
.property instance object m {
.get instance object Program/mclas::get_m()
}
So m is a property of type object which has a getter that recurses endless. The C# equivalent of this would be:
public class mclas
{
public mclas(object y) { }
public object x {
get {
return x;
}
}
}
Because the recursion never ends, taking too long and/or there is a StackOverflowException happening when the Debugger tries to evaluate m, it cancels and spits out that the evaluation timed out.
And for your actual question: I don't think that you ever need this kind of self-reference in F#, at least I can't think of any possible use.
I think that this behaviour of the compiler makes sense, because a member without paramters will always compile to a get-only property, and then this would be the most obvious way of defining an infinite recursing property (just because this has no use doesn't mean that you can't do it).
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?
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.
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.
I receive the "Recursion depth exceeded allowed limit." error when I make a breeze where condition with more than 100 conditions.
My code is
$(list).each(function () {
if (pred === undefined) {
pred = entity_ODL.create("id", "==", this.id());
}
else {
pred = pred.or("id", "==", this.id());
}
});
More than 100 conditions on a query? That sounds warning bells to me. If I were you, I'd really have a good look at what needs to be accomplished and if the current method is indeed the correct way of doing things.
The limit doesn't especially surprise me. So I think that your best bet would be to create and execute multiple queries each with less that 100 conditions and then concatenate the results. See the Q.all method for combining multiple async methods into a single callback.
you didn't specifically say, but I believe the error is actually occurring on the server side and not a breeze specific problem.
You can fix it by adding/changing the attribute of the method in your ApiController.
i.e.
[HttpGet]
[BreezeQueryable(MaxNodeCount = 10000)]
public IQueryable<EquipmentSearchView> EquipmentSearchView()
{
...
In F# its a big deal that they do not have null values and do not want to support it. Still the programmer has to make cases for None similar to C# programmers having to check != null.
Is None really less evil than null?
The problem with null is that you have the possibility to use it almost everywhere, i.e. introduce invalid states where this is neither intended nor makes sense.
Having an 'a option is always an explicit thing. You state that an operation can either produce Some meaningful value or None, which the compiler can enforce to be checked and processed correctly.
By discouraging null in favor of an 'a option-type, you basically have the guarantee that any value in your program is somehow meaningful. If some code is designed to work with these values, you cannot simply pass invalid ones, and if there is a function of option-type, you will have to cover all possibilities.
Of course it is less evil!
If you don't check against None, then it most cases you'll have a type error in your application, meaning that it won't compile, therefore it cannot crash with a NullReferenceException (since None translates to null).
For example:
let myObject : option<_> = getObjectToUse() // you get a Some<'T>, added explicit typing for clarity
match myObject with
| Some o -> o.DoSomething()
| None -> ... // you have to explicitly handle this case
It is still possible to achieve C#-like behavior, but it is less intuitive, as you have to explicitly say "ignore that this can be None":
let o = myObject.Value // throws NullReferenceException if myObject = None
In C#, you're not forced to consider the case of your variable being null, so it is possible that you simply forget to make a check. Same example as above:
var myObject = GetObjectToUse(); // you get back a nullable type
myObject.DoSomething() // no type error, but a runtime error
Edit: Stephen Swensen is absolutely right, my example code had some flaws, was writing it in a hurry. Fixed. Thank you!
Let's say I show you a function definition like this:
val getPersonByName : (name : string) -> Person
What do you think happens when you pass in a name of a person who doesn't exist in the data store?
Does the function throw a NotFound exception?
Does it return null?
Does it create the person if they don't exist?
Short of reading the code (if you have access to it), reading the documentation (if someone was kindly enough to write it), or just calling the function, you have no way of knowing. And that's basically the problem with null values: they look and act just like non-null values, at least until runtime.
Now let's say you have a function with this signature instead:
val getPersonByName : (name : string) -> option<Person>
This definition makes it very explicit what happens: you'll either get a person back or you won't, and this sort of information is communicated in the function's data type. Usually, you have a better guarantee of handling both cases of a option type than a potentially null value.
I'd say option types are much more benevolent than nulls.
In F# its a big deal that they do not have null values and do not want to support it. Still the programmer has to make cases for None similar to C# programmers having to check != null.
Is None really less evil than null?
Whereas null introduces potential sources of run-time error (NullRefereceException) every time you dereference an object in C#, None forces you to make the sources of run-time error explicit in F#.
For example, invoking GetHashCode on a given object causes C# to silently inject a source of run-time error:
class Foo {
int m;
Foo(int n) { m=n; }
int Hash() { return m; }
static int hash(Foo o) { return o.Hash(); }
};
In contrast, the equivalent code in F# is expected to be null free:
type Foo =
{ m: int }
member foo.Hash() = foo.m
let hash (o: Foo) = o.Hash()
If you really wanted an optional value in F# then you would use the option type and you must handle it explicitly or the compiler will give a warning or error:
let maybeHash (o: Foo option) =
match o with
| None -> 0
| Some o -> o.Hash()
You can still get NullReferenceException in F# by circumventing the type system (which is required for interop):
> hash (box null |> unbox);;
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions.UnboxGeneric[T](Object source)
at <StartupCode$FSI_0021>.$FSI_0021.main#()
Stopped due to error