Skip to content

Commit

Permalink
added getSingleFrom and documented getOneFrom and takeOneFrom better
Browse files Browse the repository at this point in the history
  • Loading branch information
jurgenvinju committed Feb 6, 2024
1 parent b070604 commit 85ebcc1
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 0 deletions.
45 changes: 45 additions & 0 deletions src/org/rascalmpl/library/Set.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,13 @@ public default (&T <:num) sum({(&T <: num) e, *(&T <: num) r})

@synopsis{Pick an arbitrary element from a set.}
@description{
This _randomly_ picks one element from a set, unless the set is empty.

:::warning
Use ((getSingleFrom)) if you want the element from a singleton set. ((getOneFrom)) will silently
continue even if there are more element present, which can be a serious threat to the validity of your
analysis algorithm (arbitrary data is not considered).
:::
}
@examples{
```rascal-shell
Expand All @@ -287,6 +293,16 @@ getOneFrom({"elephant", "zebra", "snake"});
getOneFrom({"elephant", "zebra", "snake"});
```
}
@benefits{
* Random sampling can be an effective test input selection strategy.
}
@pitfalls{
* The name ((getOneFrom)) does not convey randomness.
* ((getOneFrom)) drops all the other elements.
If you are sure there is only one element and you need it, then use ((getSingleFrom)). It will fail fast if your assumption is wrong.
* If you need more then one element, then repeatedly calling ((getOneFrom)) will be expensive. Have a look at ((util::Sampling)) for more effective
sampling utilities.
}
@javaClass{org.rascalmpl.library.Prelude}
public java &T getOneFrom(set[&T] st);

Expand All @@ -296,13 +312,42 @@ public java &T getOneFrom(set[&T] st);
Get "first" element of a set. Of course, sets are unordered and do not have a first element.
However, we may assume that sets are internally ordered in some way and this ordering is reproducible.
Applying `getFirstFrom` on the same set will always returns the same element.

:::warning
Use ((getSingleFrom)) if you want the element from a singleton set. ((getFirstFrom)) will silently
continue even if there are more element present, which can be a serious threat to the validity of your
analysis algorithm (arbitrary data is not considered).
:::
}
@benefits{
This function helps to make set-based code more deterministic, for instance, for testing purposes.
}
@pitfalls{
* There are much better ways to iterate over the elements of a set:
* Use the `<-` enumerator operator in a `for` loop or a comprehension.
* Use list matching
* ((getFirstFrom)) drops all the other elements
* If you are sure there is only one element and you need it, then use ((getSingleFrom)). It will fail fast if your assumption is wrong.
}
@javaClass{org.rascalmpl.library.Prelude}
public java &T getFirstFrom(set[&T] st);

@synopsis{Get the only element from a singleton set.}
@description{
Get the only element of a singleton set. This fails with a ((CallFailed)) exception when the set is not a singleton.
}
@benefits{
* getSingleFrom fails _fast_ if the assumption (parameter `st` is a singleton) is not met.
* If a binary relation `r` is injective (i.e. it models a function where each key only has one value) then all projections `r[key]` should produce singleton values: `{v}`.
Using ((getSingleFrom)) to get the element out makes sure we fail fast in case our assumptions were wrong, or they have changed.
* Never use ((getFirstFrom)) or ((takeOneFrom)) if you can use ((getSingleFrom)).
}
@pitfalls{
* ((CallFailed)) exceptions are sometimes hard to diagnose. Look at the stack trace to see that it was ((getSingleFrom))
that caused it, and then look at the paramete of ((CallFailed)) to see that the set was not a singleton.
}
public &T getSingleFrom(set[&T] st) = getFirstFrom(st) when size(st) == 1;

// TODO temporary? replacement due to unexplained behaviour of compiler
//public &T getFirstFrom({&T f, *&T _}) = f;
//public &T getFirstFrom(set[&T] _:{}) { throw EmptySet(); }
Expand Down
7 changes: 7 additions & 0 deletions src/org/rascalmpl/library/lang/rascal/tests/basic/Sets.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ test bool tst_takeOneFrom(set[int] S) {
return x in S && x notin S2 && size(S2) == size(S) - 1 && S2 < S;
}

test bool tst_getSingleFrom(set[int] S) {
if ({e} := S) {
return getSingleFrom(s) == e;
}
return false;
}

test bool tst_toList(set[int] S) = isEmpty(S) || size(S) == size(toList(S)) && all(x <- S, x in toList(S));

test bool tst_toMap(rel[int, int] S) = isEmpty(S) || domain(S) == domain(toMap(S)) && range(S) == {*toMap(S)[k] | k <- toMap(S)};
Expand Down

0 comments on commit 85ebcc1

Please sign in to comment.