Updating a single field in a record with Haskell # - haskell

I need to update one field of a very large default record.
As the default may change I don't want to rebuild the entire record manually.
Now I have come across the following way of doing this, but I am not sure how it works:
unaggregate :: MyResult -> MyResult
unaggregate calc#MyResult{..} = calc{ the_defaults = the_override
`mappend` the_defaults }
where
the_override = create ("aggregation" := False)
I have tried searching for 'Haskell # operator' in Google but it does not return immediately useful information.
I saw somewhere calc#MyResult{..} does pattern matching on variables but I don't see what variable calc does for the MyResult record...
Also I have looked up mappend (and Monoids) and I am not sure how these work either...
Thank you for any help

The # symbol is called an "as-pattern". In the example above, you can use calc to mean the whole record. Usually you'd use it like this: calc#(MyResult someResult) -- so you can have both the whole thing and the pieces that you're matching. You can do the same thing with lists (myList#(myHead:myTail)) or tuples (myTuple#(myFst, mySnd). It's pretty handy!
MyResult{..} uses RecordWildcards. Which is a neat extension! BUT RecordWildcards doesn't help you update just one field of a record.
You can do this instead: calc { theFieldYouWantToUpdate = somethingNew }.

Related

Converting match object to string in perl6

I was trying to convert a match object to a string in perl6. The method Str on a match object is defined as:
method Str(Match:D: --> Str:D)
I would think I could use Str($match) to accomplish this. And it seems to convert it to a string, but I'm getting an error using the string with the following code:
my $searchme = "rudolph";
my $match = $searchme ~~ /.*dol.*/;
say $match.WHAT;
my $test1 = Str($match);
say $test1.WHAT;
say $test1;
With the output:
(Match)
(Str)
With the error:
Cannot find method 'gist': no method cache and no .^find_method in
block at .code.tio line 6
However, if I run:
my $searchme = "rudolph";
my $match = $searchme ~~ /.*dol.*/;
say $match.WHAT;
my $test1 = $match.Str;
say $test1.WHAT;
say $test1;
I get no error and the result:
(Match)
(Str)
rudolph
Is this a bug or me misunderstanding how it works?
Thanks for reading.
I'm writing this up as an answer even though it's actually an incomplete discussion of a bug, so not at all normal SO fare. The alternative of lots of comments doesn't seem better.
It's a bug. Perhaps you just golfed this.
dd $test1; instead of say $test1; is helpful in that it displays BOOTStr $test1 = (BOOTStr without .perl method).
Based on that I searched the rakudo repo for BOOTStr and that led to the above issue.
Golfing it further leads to:
say $ = Str(Match.new);
Note that these are all fine:
say Str(Match.new);
say $ = Int(Match.new);
say $ = Str(Date.new: '2015-12-31');
It appears to be a combination of leaking some implementation details regarding how Rakudo/NQP/MoarVM bootstrap; Match being an NQP object; Str() on that being wonky; and assigning it to a Scalar container (the $ is an anonymous one) making that wonkiness visible.
I'll add more when/if I figure it out.

writing an excel formula with multiple options for a single parameter

I would like to access information from a HTTP based API and manipulate it with excel.
The API returns about 20 pieces of information, and you can get that information by looking up any number of about ten lookup fields: name, serial number etc.
I want to write a function similar to the Match Function in excel where one of the parameters (in this case MATCH TYPE) has multiple possible values.
I have a list of values (the 20 pieces of information the API can return) and I want to make these pieces of information the possible return values for one of the Functions parameters.
How do I do I create a function where one parameter has a list of possible values?
And how do I add tooltip help statements to those parameter options so people know what they are?
You want to use an Enum.
In the declarations part of your module (before any subs or functions) you can place code like this.
Enum MyFunctionsArgValue
LessThan
Equal
GreaterThan
End Enum
This will assign each of these keywords an integer value, starting at zero and counting up. So LessThan = 0, Equal = 1, and GreaterThan = 2. (You can actually start at any number you want, but the default is usually fine.)
Now you can use it in your function something like this.
Function MySuperCoolFunction(matchType as MyFunctionsArgValue)
Select Case matchType
Case LessThan
' do something
Case Equal
' do it different
Case GreaterThan
' do the opposite of LessThan
End Select
End Function
To get the tool tip, you need to use something called an Attribute. In order to add it to your code, you'll need to export the *.bas (or *.cls) file and open it in a regular text editor. Once you've added it, you'll need to import it back in. These properties are invisible from inside of the VBA IDE. Documentation is sketchy (read "nonexistent"), so I'm not sure this works for an Enum, but I know it works for functions and module scoped variables.
Function/Sub
Function MySuperCoolFunction(matchType as MyFunctionsArgValue)
Attribute MySuperCoolFunction.VB_Description = "tool tip text"
Module Scoped Var
Public someVar As String
Attribute someVar.VB_VarDescription = "tooltip text"
So, you could try this to see if it works.
Enum MyFunctionsArgValue
Attribute MyFunctionsArgValue.VB_VarDescription = "tool tip text"
LessThan
Equal
GreaterThan
End Enum
Resources
https://www.networkautomation.com/automate/urc/resources/help/definitions/Sbe6_000Attribute_DefinintionStatement.htm
http://www.cpearson.com/excel/CodeAttributes.aspx

how in matlab check for existence of a structure with fields in the workspace

I have a string and want to check if in the workspace exist any variable with the same name. In the workspace I have also many structures M.N.O M.N.N M.N.M etc. I can only check if there exist a variable with the name M. How to go deeper into this structure?
I tried:
exist('M.N')
YesNo = any(strcmp(who,'M.N.O'))
evalin('base','exist(''M.N.O'',''var'')')
all give me the same problem so I am stuck.
You can use isfield to check if a variable has a specific field. See the link for examples!
For your example, you'd need:
isfield(M,'N')
and if true, you can go deeper:
isfield(M.N,'O')
Notice that
isfield(M,'N.O')
won't work ;)
One option: write a recursive function to expand structures down to their leaf fields, appending the fields to a list.
(untested, conceptual code - probably won't work quite right as is)
function varlist = getStructFields(var,varlist)
if isstruct(var)
fn = fieldnames(var);
varlist = vertcat(varlist,fn); %# append fields to the list
for field = fn' %# ' create row vector; iterate through fields
varlist = getStructFields(var.(char(field)), varlist); %# recursion here
end
end
end
Then you can use the any(strcmp(who,'M.N.O')) check you already came up with.

constructing data-type instances from CSV

I have CSV data (inherited - no choice here) which I need to use to create data type instances in Haskell. parsing CSV is no problem - tutorials and APIs abound.
Here's what 'show' generates for my simplified trimmed-down test-case:
JField {fname = "cardNo", ftype = "str"} (string representation)
I am able to do a read to convert this string into a JField data record. My CSV data is just the values of the fields, so the CSV row corresponding to JField above is:
cardNo, str
and I am reading these in as List of string ["cardNo", "str"]
So - it's easy enough to brute-force the exact format of "string representation" (but writing Java or python-style string-formatting in Haskell isn't my goal here).
I thought of doing something like this (the first List is static, and the second list would be read file CSV) :
let stp1 = zip ["fname = ", "ftype ="] ["cardNo", "str"]
resulting in
[("fname = ","cardNo"),("ftype =","str")]
and then concatenating the tuples - either explicitly with ++ or in some more clever way yet to be determined.
This is my first simple piece of code outside of tutorials, so I'd like to know if this seems a reasonably Haskellian way of doing this, or what clearly better ways there are to build just this piece:
fname = "cardNo", ftype = "str"
Not expecting solutions (this is not homework, it's a learning exercise), but rather critique or guidelines for better ways to do this. Brute-forcing it would be easy but would defeat my objective, which is to learn
I might be way off, but wouldn't a map be better here? I guess I'm assuming that you read the file in with each row as a [String] i.e.
field11, field12
field21, field22
etc.
You could write
map (\[x,y] -> JField {fname = x, ftype = y}) data
where data is your input. I think that would do it.
If you already have the value of the fname field (say, in the variable fn) and the value of the ftype field (in ft), just do JField {fname=fn, ftype=ft}. For non-String fields, just insert a read where appropriate.

What's the best way to extract a single value from a Set in groovy?

If I have a Set that I know contains a single element, what's the best way to extract it? The best I can come up with is this, but it doesn't feel very groovy:
set = [1] as Set
e = set.toList()[0]
assert e == 1
If I'm dealing with a list, I've got lots of nice ways to get the element, none of which seem to work with Sets:
def list = [1]
e = list[0]
(e) = list
e = list.head()
One other possibility (which will work in Java or Groovy):
set.iterator().next()
A few alternatives, none of them very pretty:
set.iterator()[0]
set.find { true }
set.collect { it }[0]
Finally, if it's guaranteed that that set has only one item:
def e
set.each { e = it }
The underlying issue, of course, is that Java Sets provide no defined order (as mentioned in the Javadoc), and hence no ability to get the nth element (discussed in this question and this one). Hence, any solution is always to somehow convert the set to a list.
My guess is that either of the first two options involve the least data-copying, as they needn't construct a complete list of the set, but for a one-element set, that should hardly be a concern.
Since Java 8, here is another solution that will work for both Java and Groovy:
set.stream().findFirst().get()
Even when this question is quite old, I am sharing my just a bit prettier solution.
(set as List).first()
(set as List)[0]
If you need to take null into account (not the case in this question):
(set as List)?.first()
(set as List)?.get(index)
Hope it helps! :)

Resources