Pound symbol in object type, in F# [duplicate] - interface

What does # mean in type signatures like seq<#seq<'a>> compared to just seq<seq<'a>> ?

This is called flexible type. The short summary is that #type means any type that is inherited from type. So, in your concrete example seq<#seq<'a>> will be a sequence of any collections containing 'a values.
When calling a function, F# automatically casts concrete types to interfaces - so for example, you can call a function taking seq<'a> with array 'a[] as an argument. However, this does not work when you have array of arrays - because 'a[][] only implements seq<'a[]> but not seq<seq<'a>>.
For example, the following two functions return list of lengths of nested sequences:
let f1 (s:seq<seq<'T>>) = [ for i in s -> Seq.length i ]
let f2 (s:seq<#seq<'T>>) = [ for i in s -> Seq.length i ]
But only the second one can be called on lists of lists:
[ [1]; [2;3] ] |> f1
// error FS0001: The type 'int list list' is not
// compatible with the type 'seq<seq<'a>>'
[ [1]; [2;3] ] |> f2
// val it : int list = [1; 2]

Related

The function of validate input params

Definded the method to validate input params :
`validate_paramaters{[Date;Code;CodeType;Param]
if[ not (14h=abs[type[Date]]);
[
.log.info["\tError - Exiting function - Date is not of the correct type"];
: `$"Error - Date is not of the correct type"
]
];
if[ not (11h=abs[type[Code]]);
[
.log.info["\tError - Exiting function - Code is not of the correct type"];
: `$"Error - Code is not of the correct type"
]
];
if[ not (-11h=type[CodeType]);
[
.log.info["\tError - Exiting function - CodeType is not of the correct type"];
: $"Error - CodeType is not of the correct type"
]
];
:`validated
}`
But now The CodeType`s type add more addition :
-11h=type[CodeType] `also can be` 99h=type[CodeType]
Please give me a guidance how can i validate the CodeType now
Replace your not (-11h=type[CodeType]) with not any -11 99h in type CodeType
The keyword in allows you to compare the type of CodeType with a list of numeric types. This returns a boolean list. For example:
q)CodeType:`sample`dictionary!`a`b
q)-11 99h in type CodeType
01b
The any prepended to that returns a 1b if ANY members of the list are True.
q)any -11 99h in type CodeType
1b
Finally, you seem to be throwing errors if the CodeType is NOT one of the aforementioned types, so we'll prepend this all with a not.
Alternatively to Jemma's response, you could also try a more concise method:
validate:{[d;names;types]
if[count fails:key[d] where not any each types=type each d[names];
:"error - exiting - ",("," sv string fails)," are not of correct type"];
};
This first parameter of this function is a dictionary (d) where the key is the names of the arguments and the values are their corresponding values. This argument can be passed along with the names of the variables you are querying and the types they should be. Nested lists should be used if you're looking for multiple types to conform against.
For example, here is a dictionary with valid entries.
q)d:`date`code`codetype!(.z.d;`some`code`to`test;`thetypeofmycode)
q)validate[d;`date`code`codetype;(-14;11;(-11 99h))]
q)
However, you can see this returns an error if the dictionary is updated with an invalid datatype.
q)d[`code]:"wrongtype"
q)d[`date]:.z.p
q)validate[d;`date`code`codetype;(-14;11;(-11 99h))]
"error - exiting - date,code are not of correct type"
Just note that the list of types has to be in the same order as the dictionary
To incorporate your .log.info you can adapt the above function as so:
validate:{[d;names;types]if[count fails:key[d] where not any each types=type each d key d;
.lg.info"error - exiting - ",("," sv string[fails])," are not of correct type";
:"error: ",("," sv string[fails])," - type incorrect"]}
Referring to the example above, this would return the same result but this time logging the error using .log.info also.

How to “splat” the function arguments for dynamically created function

I am generating some functions in the module:
defmodule M do
funs = [do_it: [:arg1, :arg2]]
Enum.each(funs, fn {name, args} ->
args = Enum.map(args, & {&1, [], Elixir})
def unquote(name)(unquote(args)),
do: IO.inspect(unquote(args))
end)
end
The issue is the generated function obviously accepts one single argument, namely a list of size 2:
▶ M.__info__(:functions)
#⇒ [do_it: 1]
The goal is to dynamically declare the function accepting two arguments. In ruby terminology, it would be to unsplat argument list.
Is there a possibility to accomplish this without pattern matching the resulting AST for {:do_it, blah, [[ list of arguments ]]} and flattening the list manually?
You can use Kernel.SpecialForms.unquote_splicing/1 to "splice" in the args list:
defmodule M do
funs = [do_it: [:arg1, :arg2], do_it: [:arg1, :arg2, :arg3]]
Enum.each(funs, fn {name, args} ->
def unquote(name)(unquote_splicing(args)), do: :ok
end)
end
iex(1)> M.__info__(:functions)
[do_it: 2, do_it: 3]

How to prevent this Cyclic polynomial hash function from using a type constraint?

I am trying to implement the Cyclic polynomial hash function in f#. It uses the bit-wise operators ^^^ and <<<. Here is an example of a function that hashes an array:
let createBuzhash (pattern : array<'a>) =
let n = pattern.Length
let rec loop index pow acc =
if index < n then
loop (index+1) (pow-1) (acc ^^^ ((int pattern.[index]) <<< pow))
else
acc
loop 0 (n-1) 0
My problem is that the type of 'a will be constrained to an int, while i want this function to work with any of the types that work with bit-wise operators, for example a char. I tried using inline, but that creates some problems farther down in my library. Is there a way to fix this without using inline?
Edit for clarity: The function will be part of a library, and another hash function is provided for types that don't support the bit-wise operators. I want this function to work with arrays of numeric types and/or chars.
Edit 2 (problem solved) : The problem with inline was the way how i loaded the function from my library. instead of
let hashedPattern = library.createBuzhash targetPattern
I used this binding:
let myFunction = library.createBuzhash
let hashedPattern = myFunction targetPattern
that constraints the input type for myFunction to int, although the createBuzhash function is an inline function in the library. Changing the way I call the function fixed the type constraint problem, and inline works perfectly fine, as the answer below suggests.
In the implementation, you are converting the value in the array to an Integer using the int function as follows: int pattern.[index]
This creates a constraint on the type of array elements requiring them to be "something that can be converted to int". If you mark the function as inline, it will actually work for types like char and you'll be able to write:
createBuzhash [|'a'; 'b'|]
But there are still many other types that cannot be converted to integer using the int function.
To make this work for any type, you have to decide how you want to handle types that are not numeric. Do you want to:
Provide your own hashing function for all values?
Use the built-in .NET GetHashCode operation?
Only make your function work on numeric types and arrays of numeric types?
One option would be to add a parameter that specifies how to do the conversion:
let inline createBuzhash conv (pattern : array<'a>) =
let n = pattern.Length
let rec loop index pow acc =
if index < pattern.Length then
loop (index+1) (pow-1) (acc ^^^ ((conv pattern.[index]) <<< pow))
else
acc
loop 0 (n-1) 0
When calling createBuzhash, you now need to give it a function for hashing the elements. This works on primitive types using the int function:
createBuzhash int [| 0 .. 10 |]
createBuzhash int [|'a'; 'b'|]
But you can also use built-in F# hashing mechanism:
createBuzhash hash [| (1,"foo"); (2,"bar") |]
And you can even handle nested arrays by passing the function to itself:
createBuzhash (createBuzhash int) [| [| 1 |]; [| 2 |] |]

type char to type num mapping

q)type variable
returns the type num of the arguement variable.
Is there a mapping that can produce the type char from a type num or do I have to create that dictionary myself?
Ideally something like
q)typeChar 1
i
You can use .Q.t.
q).Q.t abs type `test
"s"
q).Q.t abs type 5i
"i"
Edit: Or even better just use .Q.ty
This seems to return upper case for atoms and lower case for lists.
q).Q.ty `test
"S"
q).Q.ty `test`test2
"s"
One option is to use 'key' function :
Reference: http://code.kx.com/q/ref/metadata/#key
Wiki says: Given a simple list, returns the name of the type as a symbol:
So you can make function like:
q) tyeInChar:{key x,()}
q) typeInChar 1i // output `int
q) typeInChar "s" //output `char

Elixir quote record(Turn it into a tuple) and preserve data?

When quoted using quote do: records aren't converted to tuples containing the record fields:
iex(1)> quote do: is_bitstring("blah")
{:is_bitstring, [context: Elixir, import: Kernel], ["blah"]}
iex(2)> quote do: Computer.new("Test")
{{:., [], [{:__aliases__, [alias: false], [:Computer]}, :new]}, [], [[name: "Test"]]}
iex(3)> quote do: Computer.new("Test")
{{:., [], [{:__aliases__, [alias: false], [:Computer]}, :new]}, [], [[name: "Test"]]}
iex(4)> c = Computer.new("Test")
Computer[name: "Test", type: nil, processor: nil, hard_drives: []]
iex(5)> c
Computer[name: "Test", type: nil, processor: nil, hard_drives: []]
iex(6)> quote do: c
{:c, [], Elixir}
Also, when I try doing this in my code:
defmacro computer([do: code]) do
# macro login here
# build computer record based on macro logic
computer = Computer.new(params)
quote do: unquote computer
end
I get an error:
** (CompileError) elixir/test/lib/computer_dsl_test.exs: tuples in quoted expressions must have 2 or 3 items, invalid quoted expression: Computer[name: "", type: nil, processor: nil, hard_drives: []]
I thought that records were just tuples with wrappers functions of some sort. The Elixir Getting Started guide states "A record is simply a tuple where the first element is the record module name." Is there something I am missing? Is there a function I can call on a record to get the tuple representation? I am aware of the raw: true option but I am not sure how to use that on an existing record.
Any insights?
Records are tuples. The output you see on the console is just formatted for easier inspection. You can check that records are tuples if you inspect them with raw: true:
iex(1)> defrecord X, a: 1, b: 2
iex(2)> x = X.new
X[a: 1, b: 2] # This is formatted output. x is really a tuple
iex(3)> IO.inspect x, raw: true
{X, 1, 2}
As can be seen, a record instance is really a tuple. You can also pattern match on it (although I don't recommend this):
iex(4)> {a, b, c} = x
iex(8)> a
X
iex(9)> b
1
iex(10)> c
2
The quote you are mentioning serves completely different purpose. It turns an Elixir expression into AST representation that can be injected into the rest of the AST, most often from the macro. Quote is relevant only in compile time, and as such, it can't even know what is in your variable. So when you say:
quote do: Computer.new("Test")
The result you get is AST representation of the call of the Computer.new function. But the function is not called at this point.
Just reading the error message and the elixir "getting stated" on macro definition it appears that the result of a quote has the form:
In general, each node (tuple) above follows the following format:
{ tuple | atom, list, list | atom }
The first element of the tuple is an atom or another tuple in the same representation;
The second element of the tuple is an list of metadata, it may hold information like the node line number;
The third element of the tuple is either a list of arguments for the function call or an atom. When an atom,
it means the tuple represents a variable.
Besides the node defined above, there are also five Elixir literals that when quoted return themselves (and not a tuple). They are:
:sum #=> Atoms
1.0 #=> Numbers
[1,2] #=> Lists
"binaries" #=> Strings
{key, value} #=> Tuples with two elements
My guess is that the unquote is the reverse function of quote, and so it expects as argument one of the above forms. This is not the case for the computer record.
I think the unquote is not necessary there (although I didn't try to understand the intent of your code...) and that
defmacro computer([do: code]) do %% why do you need this argument?
quote do: Computer.new
end
should be ok.