ocaml printf function: skip formatting entirely if some condition holds - printf

(extracted from ocaml: exposing a printf function in an object's method, so it can be answered independently)
I have the following (simplified) ocaml code, for a logger:
type log_level =
| Error
| Warn
| Info
let ord lvl =
match lvl with
| Error -> 50
| Warn -> 40
| Info -> 30
let current_level = ref (ord Warn)
let logf name lvl =
let do_log str =
if (ord lvl) >= !current_level then
print_endline str
in
Printf.ksprintf do_log
The logf function can be used with a printf format, as in:
logf "func" Warn "testing with string: %s and int: %d" "str" 42;
Is there any way to achieve the typical logging behaviour of only formatting arguments when they're actually needed? i.e something like:
let logf name lvl <args> =
if (ord lvl) >= !current_level then
Printf.printf <args>
I'm thinking it's because only the compiler knows how many arguments there will be in a format expression, and I guess there's no such thing as varargs in ocaml? So you can't ever define the full body of a printf function, you can only use currying and let the compiler magic figure it out. Is there any way to achieve what I want? Perhaps with mkprintf?

The continuation functions such as Printf.kfprintf are provided specifically to allow format wrappers such as this. It looks something like this:
open Printf
type log_level = Error | Warn | Info
let ord = function Error -> 50 | Warn -> 40 | Info -> 30
let string_of_lvl = function
| Error -> "error"
| Warn -> "warn"
| Info -> "info"
let current_level = ref (ord Warn)
let printf_with_info name lvl =
kfprintf fprintf stdout "[<%s>] <%s>: " name (string_of_lvl lvl)
let logf name lvl =
if ord lvl >= !current_level then match lvl with
| Error | Warn -> printf
| Info -> printf_with_info name lvl
else
ifprintf stdout

This seems to get me some of the way:
let logf name lvl =
if (ord lvl) >= !current_level then
Printf.fprintf stdout
else
Printf.ifprintf stdout
I'm surprised, as I thought this might mean !current_level would be evaluated too early (at module load time or something). But changing the current_level at runtime does seem to work correctly. I guess currying isn't pure like in haskell (which makes sense, given the impurity above ;)).
But it restricts me to using the fprintf family. I'd prefer to use ksprintf, because I also want a formatter, which (depending on log level) may add information like:
`[<name>] <level>: <message>`
Perhaps I could achieve this by concatenating formats (using ^^), but I don't see how.

Whatever you do, calling any Printf.*printf funciton will parse the format string at runtime. This may change in 4.02. Or you can use some syntax extension for conditional printing.

Related

Kusto error - has_any(): failed to cast argument 2 to scalar constant

I am trying to use has_any in sentinel to pass a list (comma delimited) of IPs to a query in a workbook.
The IP values will be passed into the query from a workbook parameter that the user enters.
With the below test code, if I use BadIPList variable for the has_any expression, I get the error "has_any(): failed to cast argument 2 to scalar constant"
If I use BadIPList2 it works fine, although they should be the same once I convert BadIPList to a Dynamic type.
let StartTime = "2022-08-07";
let TimeOffset = 4d;
let BadIPList = '10.1.1.100,10.1.1.102,10.1.1.110,10.1.1.120';
let BadIPlist2 = dynamic(['10.1.1.100','10.1.1.102','10.1.1.110','10.1.1.120']);
DeviceNetworkEvents
| extend BadIPList=todynamic(split(BadIPList,","))
| where TimeGenerated between (startofday(todatetime(StartTime)) .. endofday(todatetime(StartTime) + TimeOffset))
//next line errors
//| where RemoteIP has_any(BadIPList)
//next line works
| where RemoteIP has_any(BadIPlist2)
| project RemoteIP, BadIPList, BadIPlist2
| take 10
//verify variable types
| extend ipType = gettype(BadIPList), ipType2 = gettype(BadIPlist2)
| getschema
output of BadIPList2
I have checked the types of the two variables (Using gettype and getschema), and they seem to be the same any ideas about what I have done wrong?
DataTypes for variables
As the error message notes, the parameter should be a "scalar constant" (and not a row-level expression)
// Data sample generation. Not part of the solution.
let DeviceNetworkEvents = datatable(RemoteIP:string)["yada yada 10.1.1.102 yada"];
// Solution starts here
let BadIPList = '10.1.1.100,10.1.1.102,10.1.1.110,10.1.1.120';
let BadIPList_split = split(BadIPList, ",");
DeviceNetworkEvents
| where RemoteIP has_any(BadIPList_split)
RemoteIP
yada yada 10.1.1.102 yada
Fiddle

Is there a way to pass the name of a field to a setter function?

Here I have several functions that all just set a single field on a model record.
In a more dynamic language, I'd just have a single setter function and pass it the name of the field (as a string) and the value that I want to set on the model object.
Is there a way to pass the name of the field in Elm?
What's the Elm way of doing something like this?
type alias Patient =
{ id : String
, name : String
, dateOfBirth : String
, sex : String
... other fields
}
setPatientName : Patient -> String -> Patient
setPatientName patient value =
{ patient | name = value }
setPatientDateOfBirth : Patient -> String -> Patient
setPatientDateOfBirth patient value =
{ patient | dateOfBirth = value }
setPatientSex : Patient -> String -> Patient
setPatientSex patient value =
{ patient | sex = value }
... many others
-- idx is the index of the patient in the model (which is an array of patients)
-- UpdateCell is a variant of my Msg type, like this: UpdateCell Int (Patient -> String -> Patient) String
onInputHandler : Int -> (Patient -> String -> Patient) -> String -> Msg
onInputHandler idx setter inputText =
UpdateCell idx setter inputText
-- idx is the index of the patient in the model (which is an array of patients)
createTableRow : Int -> Patient -> Html Msg
createTableRow idx patient =
...
, input [ type_ "text", onInput (onInputHandler idx setPatientName), value patient.name ] []
, input [ type_ "text", onInput (onInputHandler idx setPatientDateOfBirth), value patient.dateOfBirth ] []
...
I'm currently using each of these functions as an event handler for input elements. So I need a function that I can use for handling the input event. Ideally, I'd define just a single function and use that single one for all the input elements and pass it the field I want to update on the patient record.
The short answer is "no". But this seems a bit like an XY problem. It's not clear what benefit you are trying to achieve since the full application of such a function would be longer than the equivalent record update expression:
setField "name" patient value
-- vs
{ patient | name = value }
and as a partially applied function is only slightly shorter than the equivalent anonymous function with shortened argument names:
setField "name"
-- vs
\r x -> { r | name = x }
Although the latter is significantly noisier with all the symbols.
There is also a short-hand function for getting a record field:
.name
-- vs
\r -> r.name
So there is some precedent for having a dedicated syntax for setter functions too, but unfortunately there is not. Likely because it would complicate the language, and the syntax in particular, for relatively little benefit. I'm therefore curious about what you're actually trying to accomplish.
Edit after question update:
Putting functions in the Msg is a very bad idea because it goes against the Elm Architecture. It makes the state transition opaque and won't work very well with the debugger. When something goes wrong you can still see the state before and after, but you'll have trouble understanding what happened, and why it happened, because that information is encoded in an opaque function which probably isn't the one it should be.
You'll also have trouble factoring your logic. If you need something to happen only when a certain field updates, you might have to put the logic in the view, or special-case that field by putting the logic for that in update while the rest is in view, for example. Either way, you're on the path to a messy code base.
You should generally use names for messages that describe what happened, not what to do, because that tends to lead to an imperative mindset. Instead of UpdateCell you could call it InputChanged, for example. Then instead of the function you should have an identifier for the field. Ideally a custom type, like InputChanged Name, but even a string will work, though it will be much easier to miss a typo.
So instead of setter functions for each field you'll just case match the message and set the field in the update function:
InputChanged Name value ->
{ patient | name = value }
-- vs
setPatientName : Patient -> String -> Patient
setPatientName patient value =
{ patient | name = value }
Then if you need to clear the sex when the name changes, for example (because reasons...), you can simply do:
InputChanged Name value ->
{ patient | name = value, sex = "" }
The Elm Architecture is good because it makes changes easy and safe, not because it's concise and free of boiler-plate. Good Elm code often has a lot of copy-and-paste, but that's not always bad.

DBeaver, How to declare variables and use them?

i just want to know if it is possible to declare variables on the DBeaver´s sql editor and use them on a query
You have to enable variable processing in the "SQL Processing" settings of DBeaver -> Window -> Preferences -> Database -> Editors -> SQL Editor -> SQL Processing. There is a block on Parameters with settings you can change. See the Dynamic Parameter binding section on the wiki.
You should then be able to do:
#set date = '2019-10-09'
SELECT ${date}::DATE, ${date}::TIMESTAMP WITHOUT TIME ZONE
which produces:
| date | timestamp |
|------------|---------------------|
| 2019-10-09 | 2019-10-09 00:00:00 |
Yes you can, using :.
An example:
SELECT * FROM "SYSIBM".SYSDUMMY1
WHERE IBMREQD = :YOUR_VARIABLE
Based on the incredibly helpful post from #nicoschl, here are a couple of minor improvements:
-- using declarations
#set datex_start = cast('2120-01-01' as date) as date_start;
-- datex_start is the var name
-- casting the value in the declaration saves us the work later
-- the var can be given a default fieldname (e.g. "date_start")
-- run as a standalone command since the subsequent SELECT statement doesn't return values when it's all run together
select
${datex_start}
;
This will return a value "2120-01-01" with a fieldname of "date_start".
In the DBeaver SQL editor you can type the following:
-- define variable
#set my_var='hello SQL world'
-- call variable
select :my_var
You can also use ${my_var} to reference the variable; $my_var however did not work for me. I am using DBeaver v. 21.1.
You have to enable at Dbeaver settings:
Top Window > Preferences > and then see print below (updated 2022/08).

Force FsCheck to generate NonEmptyString for discriminating union fields of type string

I'm trying to achieve the following behaviour with FsCheck: I'd like to create a generator that will generate a instance of MyUnion type, with every string field being non-null/empty.
type MyNestedUnion =
| X of string
| Y of int * string
type MyUnion =
| A of int * int * string * string
| B of MyNestedUnion
My 'real' type is much larger/deeper than the MyUnion, and FsCheck is able to generate a instance without any problem, but the string fields of the union cases are sometimes empty. (For example it might generate B (Y (123, "")))
Perhaps there's some obvious way of combining FsCheck's NonEmptyString and its support for generating arbitrary union types that I'm missing?
Any tips/pointers in the right direction greatly appreciated.
Thanks!
This goes against the grain of property based testing (in that you explicitly prevent valid test cases from being generated), but you could wire up the non-empty string generator to be used for all strings:
type Alt =
static member NonEmptyString () : Arbitrary<string> =
Arb.Default.NonEmptyString()
|> Arb.convert
(fun (nes : NonEmptyString) -> nes.Get)
NonEmptyString.NonEmptyString
Arb.register<Alt>()
let g = Arb.generate<MyUnion>
Gen.sample 1 10 g
Note that you'd need to re-register the default generator after the test since the mappings are global.
A more by-the-book solution would be to use the default derived generator and then filter values that contain invalid strings (i.e. use ==>), but you might find it not feasible for particularly deep nested types.

Pattern match a polymorphic type in a concrete type in Elm

I'm using elm-form: https://github.com/etaque/elm-form/, and I need to pattern match a polymorphic type into a concrete type, however I got the next error:
The pattern matches things of type:
TranslationId
But the values it will actually be trying to match are:
e
Hint: Your type annotation uses type variable `e` which means any type of value
can flow through. Your code is saying it CANNOT be anything though! Maybe change
your type annotation to be more specific? Maybe the code has a problem? More at:
<https://github.com/elm-lang/elm-compiler/blob/0.18.0/hints/type-annotations.md>
The code in question is this:
translateError : ErrorValue e -> String
translateError error =
case error of
InvalidEmail ->
translate English ErrInvalidEmail
Empty ->
translate English ErrEmpty
CustomError PasswordNotMatch ->
translate English PasswordNotMatch
x ->
toString x
ErrorValue type https://github.com/etaque/elm-form/blob/f9480cb8646ebc9f78f13d3a7482c463c5275776/src/Form/Error.elm#L19:
type ErrorValue e
= Empty
| InvalidString
| InvalidEmail
| InvalidUrl
| InvalidFormat
| InvalidInt
| InvalidFloat
| InvalidBool
| InvalidDate
| SmallerIntThan Int
| GreaterIntThan Int
| SmallerFloatThan Float
| GreaterFloatThan Float
| ShorterStringThan Int
| LongerStringThan Int
| NotIncludedIn
| CustomError e
TranslationId type https://github.com/werner/madison-elm/blob/master/src/elm/Translations/Utils.elm#L9:
type TranslationId
= ErrInvalidEmail
| PasswordNotMatch
| ErrEmpty
I came up with a solution, but it looks weird and I'm not sure if it's the right one https://github.com/werner/madison-elm/blob/master/src/elm/Translations/FormErrors.elm#L7:
translateError : ErrorValue e -> String
translateError error =
case error of
InvalidEmail ->
translate English ErrInvalidEmail
Empty ->
translate English ErrEmpty
CustomError e ->
case (toString e) of
"PasswordNotMatch" ->
translate English PasswordNotMatch
x ->
toString x
x ->
toString x
As #reactormonk says, you are not addressing the type variable in your type definition. elm-form provides flexibility in the custom error through this type variable, which you have to supply yourself if you want to use custom errors (if you do not then you can use the variable throughout your code with no problem).
In particular ErrorValue has a type variable e that you need to specify: it does not matter in the code that does not use the CustomError constructor, but does matter in translateError because you are trying to pattern match on CustomError. It looks as though the type you want is TranslationId so you want
translateError : ErrorValue TranslationId -> String
translateError error =
case error of
InvalidEmail ->
translate English ErrInvalidEmail
Empty ->
translate English ErrEmpty
CustomError PasswordNotMatch ->
translate English PasswordNotMatch
x ->
toString x