I just started learning Haskell at uni, and while playing around with it, I stumbled upon a problem that I can't seem to grasp.
The following code gives me the desired result:
import Data.List
list = ["Hello", "world"]
main = print $ intercalate " something " (reverse (map reverse list))
Output:
"dlrow something olleH"
But I want to write the 'main' function with dots instead of brackets, so it tried:
main = print $ intercalate " something " . reverse . map reverse list
However, this gives me the following errors:
test.hs:5:54: error:
• Couldn't match expected type ‘a0 -> [[Char]]’
with actual type ‘[[Char]]’
• Possible cause: ‘map’ is applied to too many arguments
I thought that these dots meant exactly the same as the brackets: function composition. Why do the brackets work, whereas the dots give me a type-related error? Any help would be greatly appreciated!
Brackets don't mean function composition. They just mean “group this subexpression”. Of course you can use them to make a chain of function compositions: the following defines c as a composition of the functions f, g and h
c x = f (g (h x))
This could also be written as:
c = f . g . h
Thus, you could write
main = print $ c list
where c = intercalate " something " . reverse . map reverse
But if you then inline c again, you need to be careful not to mess up the parsing rules: just writing list to the right of that composition chain will not do, because function application binds more tightly than any infix operator (including ., though that is in fact the tightest infix operator). I.e.,
intercalate " something " . reverse . map reverse list
is actually parsed as
(intercalate " something ") . (reverse) . (map reverse list)
But that's not what you wanted. You need to make sure that list is actually the argument to the entire composition chain, not just its last element; the preferred way to do that is with the $ operator:
intercalate " something " . reverse . map reverse $ list
$ has the lowest prevedence, thus this is parsed as
((intercalate " something ") . (reverse) . (map reverse)) (list)
Alternatively, you can apply map reverse to list right away – this by itself isn't wrong, just the result isn't part of the composition chain anymore:
intercalate " something " . reverse $ map reverse list
The equivalence of function composition goes as follows:
main = print (intercalate " something " (reverse (map reverse list)))
main = print (intercalate " something " ((reverse . map reverse) list))
main = print ((intercalate " something " . (reverse . map reverse)) list)
main = (print . (intercalate " something " . (reverse . map reverse))) list
or, by dropping the unnecessary parentheses:
main = (print . intercalate " something " . reverse . map reverse) list
main = print . intercalate " something " . reverse . map reverse $ list
In your attempt, map reverse list is a single expression, the argument of the infix . operator, which doesn't work - you only can compose the map reverse function, and then apply the whole composed function to the list argument.
Function composition using . implies that there will be another parameter to the function. Had you instead written the following, it would work.
main = print $ (intercalate " something " . reverse . map reverse) list
That way, the list value is a parameter to the function composed inside parentheses.
Related
I am trying to form where clause from the JSON file. I want to fetch the key:value pair from the where part of below input and convert it into key = value
Input:
{
"Table":{
"TableName":"Employee",
"Columns":[
"ID",
"Name"
],
"Where":{
"ID":"A-0001",
"Name":"xyz"
}
}
}
Expected Output:
I want to get where ID = 'A-0001' and Name = 'xyz'
I am using mule 4. Please help.
Thanks in advance
Another take at a similar approach.
%dw 2.0
output application/json
---
"where " ++ ((payload.Table.Where mapObject {
($$): (($$) ++ "=" ++ "'" ++ ($) ++ "'")
}) pluck ($) joinBy " and ")
Building dynamic SQL? I would suggest avoiding that and sticking to parameterized queries.
I would strongly urge you to use this path: https://docs.mulesoft.com/connectors/db/database-connector-examples#use-input-parameters-to-protect-database-queries
It would be really easy to make this parameterized because all you'd have to do is passing in payload.Table.Where as your parameterized object.
If you're pulling this from some kind of trusted source and have to do things this way I suppose you could ignore the columns array and do it this way pretty easily:
%dw 2.0
output application/json
---
if (payload.Table.Where?)
"where " ++ ((payload.Table.Where pluck "$($$) = '$($)'") reduce ($$ ++ " and " ++ $))
else
""
I'm sure there is a better way to do this, but if you wanted to still use the columns array you could also do this.
%dw 2.0
output application/json
---
"where " ++ (
payload.Table.Columns reduce ((col, wClause="") ->
if (not payload.Table.Where[col]?) wClause
else wClause ++ (if (wClause != "") " and " else "") ++ ("$(col) = '$(payload.Table.Where[col])'")
)
)
Ran into a strange interaction between print and the ternary conditional operator that I don't understand. If we do...:
print 'foo, ' . (1 ? 'yes' : 'no') . ' bar';
...then we get the output...:
foo, yes bar
...as we would expect. However, if we do...:
print (1 ? 'yes' : 'no') . ' bar';
...then we just get the output...:
yes
Why isn't " bar" getting appended to the output in the second case?
Let's do it, but for real -- that is, with warnings on
perl -we'print (1 ? "yes" : "no") . " bar"'
It prints
print (...) interpreted as function at -e line 1.
Useless use of concatenation (.) or string in void context at -e line 1.
yes
(but no newline at the end)
So since (1 ? "yes" : "no") is taken as the argument list for the print function then the ternary is evaluated to yes and that is the argument for print and so that, alone, is printed. As this is a known "gotcha," which can easily be done in error, we are kindly given a warning for it.
Then the string " bar" is concatenated (to the return value of print which is 1), what is meaningless in void context, and for what we also get a warning.
One workaround is to prepend a +, forcing the interpretation of () as an expression
perl -we'print +(1 ? "yes" : "no") . " bar", "\n"'
Or, call the print as function properly, with full parenthesis
perl -we'print( (1 ? "yes" : "no") . " bar", "\n" )'
where I've added the newline in both cases.
See this post for a detailed discussion of a related example and precise documentation links.
If the first non-whitespace character after a function name is an opening parenthesis, then Perl will interpret that as the start of the function's parameter list and the matching closing parenthesis will be used as the end of the parameter list. This is one of the things that use warnings will tell you about.
The usual fix is to insert a + before the opening parenthesis.
$ perl -e "print (1 ? 'yes' : 'no') . ' bar'"
yes
$ perl -e "print +(1 ? 'yes' : 'no') . ' bar'"
yes bar
I am just wondering what happens with that piece of code.
Why the result is incorrect only when printed directly, why is the newline ignored?
user#host_09:22 AM: perl
print 2 >> 1, "\n";
print 2 & 2, "\n";
print (2 & 2) >> 1, "\n";
1
2
2user#host_09:22 AM: perl
$a = (2 & 2) >> 1;
print "$a\n";
1
Perl interprets the parentheses as function arguments marker, as you can verify with
perl -MO=Deparse,-p -e 'print (2 & 2) >> 1'
Output:
(print(2) >> 1);
The canonical way is to precede the left parenthesis with a +:
print +(2 & 2) >> 1
When you print it with warnings it becomes clear(er)
perl -we'print (2 & 2), "\n"'
says
print (...) interpreted as function at -e line 1.
Useless use of a constant ("\n") in void context at -e line 1.
It works out print (2&2) as a function call to print† and duly prints 2 (no newline!), and then it keeps evaluating the comma operator, with "\n" in void context next, which it also warns us about.
With >> 1 also there, the return 1 of print (2&2) (for success) is bit shifted to 0, which disappears into the void, and we get
another "Useless use of ... in void context."
One fix is to add a + since what follows it must be an expression
perl -we'print +(2 & 2) >> 1, "\n"'
Or, make a proper call to print, with parenthesis around the whole thing
perl -we'print((2 & 2) >> 1, "\n")'
Both print a line with 1.
This is mentioned in print, and more fully documented in Terms and List operators and in Symbolic Unary operators, both in perlop. For another, related, example see this post.
† It also warns about it as it is likely an error -- with a space before parens; no space, no warning.
Please have a look to the below vim function which I written in my /.gvimrc file.
The function id for deleting the "n" number of last characters in each line from the range of lines specified by "start_line" and "end_line".
function RLNC (n, start_line, end_line)
execute . a:start_line . "," . a:end_line . "s/.\{" . a:n . "}$//"
endfunction
but when I make the same as a function and call it in the vim
:call RLNC(3, 128, 203)
This is the actual operation I am doing here
:start_line,end_lines/.\{n}$//
This is nothing but
:128,203s/.\{3}$//
Please help me to find what is going wrong..?
its is giving errors
The error is:
E15: Invalid expression: . a:start_line . "," . a:end_line . "s/.\{" . a:n . "}$//"
So, the first period is suspect. The :execute command takes (one or multiple) expressions. String concatenation via . is only done between strings, not at the beginning.
Just leave off the first .:
execute a:start_line . "," . a:end_line . "s/.\{" . a:n . "}$//"
The manual concatenation is tedious. Better use printf():
execute printf("%d,%ds/.\{%d}$//", a:start_line, a:end_line, a:n)
The next problem is that inside double quotes, the backslash must be escaped (doubled). Better use single quotes:
execute printf('%d,%ds/.\{%d}$//', a:start_line, a:end_line, a:n)
Finally, Vim has a special syntax to pass a range to a function. See :help function-range-example. You do not need to use this, but it makes the invocation more natural:
:128,203call RLNC(3)
However, I would probably go ahead and define a custom command wrapping the function.
:command! -range -nargs=1 RLNC call RLNC(<args>, <line1>, <line2>)
If your function isn't actually more complex, we can now inline this and get rid of the function altogether:
:command! -range -nargs=1 RLNC execute printf('%d,%ds/.\{%d}$//', <line1>, <line2>, <args>)
(Note that without a function, the last search pattern gets clobbered.)
Thank for your reply, I am new to vim function and all. So I don't know much about the ":command!" and all. So I put it as function in the /.gvimrc file like below :
function RLNC (start_line, end_line, n)
if (a:start_line <= a:end_line)
execute printf(':%d,%ds/.\{%d}$//', a:start_line, a:end_line, a:n)
else
execute printf('Start line %d is more than End line %d ', a:start_line, a:end_line)
endif
endfunction
and its working fine when I use the :call RLNC(128, 203, 3) in my gvim files.
Thanks You
I ran into some strange behavior writing a .ps1 script. I wrote a function that takes two parameters, but for some reason the second parameter was always null.
Upon closer inspection, it seems like my two parameters are somehow getting collapsed into the first one.
Given the following script, I would have expected, one line of output showing ...
function Foo($first, $second) {
echo $first
}
$x = "..."
$y = "why?"
Foo($x, $y)
But when I run this script, I get
...
why?
Is there some PowerShell syntax I don't know about that I'm accidentally (mis-)using?
Do not use parens around your arguments and don't use commas to separate arguments. Invoke your functions just like you would any other PowerShell command - using space separated arguments e.g.:
foo $x $y
When you put parens around ($x, $y) PowerShell passes that as a single expression/argument, in this case an array containing two items to the first parameter ($x) of your function. You can use Strict-Mode -Version Latest to warn you when you do this e.g.:
114> function foo($x,$y){}
115> foo(3,4)
116> Set-StrictMode -Version latest
117> foo(3,4)
The function or command was called as if it were a method. Parameters should be separated by
spaces. For information about parameters, see the about_Parameters Help topic.
At line:1 char:1
+ foo(3,4)
+ ~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : StrictModeFunctionCallWithParens
Is there some PowerShell syntax...that I'm accidentally (mis-)using?
Yes, you are not calling your function properly. In PowerShell, function calls do not use parenthesis or have their arguments separated by commas.
Instead, you would call Foo like so:
Foo $x $y
See a demonstration below:
PS > function Foo($first, $second) {
>> echo $first
>> }
>>
PS > $x = "..."
PS > $y = "why?"
PS > Foo $x $y
...
PS >
PS > function Foo($first, $second) {
>> echo "$first and $second"
>> }
>>
PS > Foo $x $y
... and why?
PS >
In case you are wondering, your current code has PowerShell interpreting ($x, $y) as a single argument to Foo: a two-item array. Thus, it assigns this array to $first and $null to $second:
PS > function Foo($first, $second) {
>> echo "Type of `$first: $($first.Gettype())"
>> echo "`$second is `$null: $($second -eq $null)"
>> }
>>
PS > $x = "..."
PS > $y = "why?"
PS > Foo($x, $y)
Type of $first: System.Object[]
$second is $null: True
PS >
The "," is used differently in powershell than in other programming languages. Here it is the array constructor.
(1,2,3).GetType() # System.Object[]
(,'element').GetType() # System.Object[]
Because you havent specified a datatype on your parameters the powershell assumes it is dealing with plain old System.Objects (the superclass of all classes). It then takes the array and assigns it to the first input parameter because it is the only one in this line. It can do that because an array is, by extension, also a System.Object.
Also, the old synthax for defining functions is not recommended anymore:
function foo ( $first, $second ) {}
When writing this, the powershell interpreter would internally convert this into an advanced function:
function foo {
PARAM(
[Parameter(Position=1)]
[object]$first,
[Parameter(Position=2)]
[object]$second
)
BEGIN { <# Do stuff here #> }
PROCESS { <# Do stuff here #> }
END { <# Do stuff here #> }
}
foo -first 'first' -second 'second'
foo 'first' 'second'
Causing unneeded overhead.
I hope that clears it up a little :)