How to annotate let binding as deprecated in OCaml? - annotations

I want to annotate a function from an external library as deprecated, to ensure it will not be use in my project. Let's assume the library offers the following module:
module Lib : sig
val safe_function : int -> unit
val unsafe_function : int -> int -> unit
end = struct
let safe_function _ = ()
let unsafe_function _ _ = ()
end
I have a Util.ml file in my project, which I open in every file. In it, I would like to do something like that:
open Lib
let unsafe_function = Lib.unsafe_function
[##deprecated "Use safe_function instead."]
let foo = (fun x -> x)
[##deprecated "BBB"]
type t =
| A [#deprecated]
| B [#deprecated]
[##deprecated]
Compiling the following usage.ml file
open Util
let _ = unsafe_function 0 0
let _ = foo 0
let _ = A
let f (x : t) = x
produces the following warnings:
$ ocamlc -c -w +3 usage.ml
File "usage.ml", line 6, characters 8-9:
Warning 3: deprecated: A
File "usage.ml", line 7, characters 11-12:
Warning 3: deprecated: Util.t
So the deprecated attributes on the let-bindings do not trigger, but the ones on the type definition and constructors do. The attribute syntax seems to allow both.
I found this answer but it seems to be outdated because:
It explicitly says that it "is only available for values (not on types)", which is not true (anymore?) as shown by the above example.
The documentation explicitly says the annotation "can be applied to most kind of items in signatures or structures."

I'm not sure what the exact syntax is (your suggestion sounds right and corresponds to the parser code, so it might be a bug in the compiler), but you can (ab)use the module system to do that :
include (Lib : sig
val unsafe_function : int -> int -> unit
[##ocaml.deprecated "Use safe_function instead."]
end)
let _ = unsafe_function 0 0 (* warning here *)

Just had the same issue. OCaml syntax for attributes is somewhat hairy.
This works:
let unsafe_function [#deprecated "Use safe_function instead."] =
Lib.unsafe_function
(notice the single #)

Related

Scala: Set of tuples doesn't allow me to add a "straight" tuple

I have this code of a mutable Hashmap with a mutable Set with tuples:
val field = mutable.HashMap[String, mutable.Set[(Pos3, Pos3)]]()
In foreach loop I'm populating the field
scanner.combinations(2).foreach(comb => {
val dist = s"${comb(0).dist(comb(1))}"
field += (dist -> (field.getOrElse(dist, mutable.Set()) += (comb(0), comb(1))))
}
which doesnot compile with message:
found : aoc2021.Pos3
required: (aoc2021.Pos3, aoc2021.Pos3)
field += (dist -> (field.getOrElse(dist, mutable.Set()) += (comb(0), comb(1))))
However whenever I change the bare (comb(0), comb(0)) with a value-referenced tuple:
scanner.combinations(2).foreach(comb => {
val posTuple = (comb(0),comb(1))
val dist = s"${comb(0).dist(comb(1))}"
field += (dist -> (field.getOrElse(dist, mutable.Set()) += posTuple))
}
It compiles & executes nicely. I also played with (immutable.)Sets: same story.
PS I know I should use idiomatic scala and ditch the side effects and foreach.
Remember that when you do foo += x that is just sugar syntax for foo.+=(x) (i.e. omit the dot and the parenthesis)
But when you do foo += (x, y) the compiler thinks you are just omitting the dot instead of passing a tuple which produces the error you were seeing.
The solution would be to use a double set of parenthesis like: ((comb(0), comb(1))) or using the -> extension method like: (comb(0) -> comb(1))
This is among the innumerable amount of reasons why I dislike the excessive amount of sugar syntax that the language allows.

Scala why _ (underscore) is working for first argument but not second

I am learning scala, and I am not able to understand following thing.
why following works?
val colNames = List("salary_new", "age_new", "loc_new")
val colRenameMap = colNames.map(_ -> "a").toMap
but following doesn't ? is it, that I can use _ as first argument only? how does this _ works?
val colRenameMap = colNames.map(_ -> _.split("")).toMap
Luis answered the question already, so let's convert it to a proper answer.
val colRenameMap = colNames.map(_ -> _.split("")).toMap
translates to
val colRenameMap = colNames.map((a,b) => a -> b.split("")).toMap
Each time you use _, it refers to a different argument. Use it twice, and you get a function of two arguments, instead of the single-argument function map needs. In this case, you have to be explicit and name your unique argument to be able to use it twice
val colRenameMap = colNames.map(a => a -> a.split("")).toMap
It's because of in scala underscore in different cases means different sense.
first case:
val colNames = List("salary_new", "age_new", "loc_new")
val colRenameMap = colNames.map(_ -> "a").toMap
here you can rewrite line as follows:
val colRenameMap = colNames.map(x => x -> "a").toMap
if you are use argument in lambda expression once, you can concise expression by underscore _.
but when you try to use underscore _ twice - it can't be used for that.
here you are trying to use argument in lamda expression twice:
//first //second
val colRenameMap = colNames.map(x => x -> x.split("")).toMap
and you can't concise it using underscore _

Macro expansion contains free variable

My code compiles with the following error: Macro expansion contains free term variable Hello ...
I have reduced it to minimal example:
class Hello(val hi: String) {
val xx = reify(hi)
var yy = q""
}
def setYYImpl(c: Context)(hExpr: c.Expr[Hello]): c.Expr[Hello] = {
import c.universe._
val hello = c.eval(c.Expr[Hello](c.untypecheck(hExpr.tree.duplicate)))
val xxVal = c.internal.createImporter(u).importTree(hello.xx.tree)
c.Expr(q"""{val h = new Hello("HO"); h.yy=$xxVal; h}""") // it should set `h.yy` to Tree:"HO"
}
def setYY(hExpr: Hello): Hello = macro setYYImpl
setYY(new Hello("HI"))
After inspecting similar question: Can this free-term-variable error (produced at macro expansion) be avoided?
I have come to conclusion that the problem is reify(hi) which refers to the compile time value Hello.hi.
Is it possible to work around this problem? reify(hi) returns Expr Hello.hi, can I somehow remove the Hello. prefix?
Try to replace
val xx = reify(hi)
with
val xx = Literal(Constant(hi))
i.e. build the tree manually (and
.importTree(hello.xx.tree)
with
.importTree(hello.xx)).
(If it's Literal(Constant... only in your example and more complex tree in actual use case, anyway try to build it manually rather than use reify.)
Then you'll have different error
Error: type mismatch;
found : String("HI")
required: reflect.runtime.universe.Tree
setYY(new Hello("HI"))
because your macro returns
Expr[Hello]({
val h = new Hello("HO");
h.yy = "HI"; // h.yy is q"" i.e. Tree, "HI" is String
h
})

Operators as function literals

So for a very small formula interpretter someone asked me to code-golf, I wanted to do something like
val ops = Map("plus" -> + , "minus"-> -, "times"-> *, "div" -> / )
to allow quick conversion between keywords and the functions they describe. Not only did this syntax not parse, but none of the other shorthand syntaxes I tried ( _ + _, _:Int.+_)parsed. Is there a way of doing this as a function shorthand, or am I doomed to writing out the lambdas fully, ruining my golf score.
Edit: The problem in question was integers only. I understand that overloading would make this vastly more difficult otherwise.
Not sure how your score is impacted by defining an additional function. You could lift the operators, using something like this:
def lift(f:(Int,Int)=>Int) = f
val ops = Map("plus" -> lift(_+_), "minus" -> lift(_-_), "times"-> lift(_*_), "div" -> lift(_/_))
And if you want to shave some chars:
def ↑(f:(Int,Int)=>Int) = f
val ops = Map("plus" -> ↑(_+_), "minus" -> ↑(_-_), "times"-> ↑(_*_), "div" -> ↑(_/_))
And as om-nom-nom comments, this explicit lifting can be done by the compiler as well by providing the type signature to coerse the lifting:
val ops: Map[String, (Int,Int) => Int] = Map("plus" -> (_+_), "minus" -> (_-_), "times"->(_*_), "div" -> (_/_))

Defining Entity Framework Keys using Fluent API

I am trying to define a key for a model type that has two key properties and is defined like this:
type Model () =
member val IdOne = 0 with get, set
member val IdTwo = 0 with get, set
member val OtherProperty = "" with get, set
When I try to use this model in Entity Framework 5, I get the error that "Model has no key defined. Define the key for this EntityType". The model types are given, I cannot change them and add the [<Key>] attribute. So I tried the Fluent API.
In C#, you would do something like this:
modelBuilder.Entity<Model>().HasKey(m => new { m.IdOne, m.IdTwo });
It uses an anonymous type. But for the life of me I cannot figure out how to pull this off in F#. I tried Tuples, Records, even a regular type that has the properties IdOne and IdTwo:
// Regular type with properties IdOne & IdTwo.
type ModelKey (idOne, idTwo) =
member this.IdOne = idOne
member this.IdTwo = idTwo
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> ModelKey (m.IdOne, m.IdTwo))
// ArgumentNullException: Value cannot be null. Parameter name: source
// Regular type with default constructor and properties IdOne & IdTwo.
type ModelKey2 () =
member val IdOne = 0 with get, set
member val IdTwo = 0 with get, set
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> ModelKey2 ())
// ArgumentNullException: Value cannot be null. Parameter name: source
// Record type.
type ModelKeyRecord = { IdOne : Int32; IdTwo : Int32 }
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> { IdOne = m.IdOne; IdTwo = m.IdTwo })
// ArgumentNullException: Value cannot be null. Parameter name: source
// Tuple.
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> (m.IdOne, m.IdTwo))
// ArgumentNullException: Value cannot be null. Parameter name: source
None of these approaches work, I get an ArgumentNullException every time. I'm out of ideas...
EDIT
With the code Tomas provided below (that causes the same ArgumentNullException btw.), I did some snooping around. This is what I found:
I used the function below to analyze the Expression Tree C# is building:
static void Analyze<T>(Expression<Func<Model, T>> function)
{
}
// Call it like this:
Analyze(m => new { m.IdOne, m.IdTwo });
Then I looked at the generated lambda in the debugger. This is what C# generates:
{m => new <>f__AnonymousType0'2(IdOne = m.IdOne, IdTwo = m.IdTwo)}
Doing the same thing on the F# side using the getFuncTree function from Tomas using <# fun (m : Model) -> ModelKey(m.IdOne, m.IdTwo) #> yields:
{m => new ModelKey(m.IdOne, m.IdTwo)}
As you can see, the explicit naming of the - what is this anyway, looks like properties - arguments is missing in the F# code. I recreated the whole expression tree by hand in F# so that it would look like the C# version:
let modelKeyExpression =
Expression.Lambda<Func<Model, ModelKey>> (
body = Expression.New (
``constructor`` = typeof<ModelKey>.GetConstructor [| typeof<Int32>; typeof<Int32> |],
arguments = seq {
yield Expression.MakeMemberAccess (
expression = Expression.Parameter (
``type`` = typeof<Model>,
name = "m"
),
``member`` = typeof<Model>.GetProperty "IdOne"
) :> Expression;
yield Expression.MakeMemberAccess (
expression = Expression.Parameter (
``type`` = typeof<Model>,
name = "m"
),
``member`` = typeof<Model>.GetProperty "IdTwo"
) :> Expression
},
members = seq {
yield (typeof<ModelKey>.GetProperty "IdOne") :> MemberInfo
yield (typeof<ModelKey>.GetProperty "IdTwo") :> MemberInfo
}
),
parameters = [
Expression.Parameter (
``type`` = typeof<Model>,
name = "m"
)
]
)
The part that was missing in the F# representation is the members sequence.
When I move the mouse over this expression, this representation appears:
{m => new ModelKey(IdOne = m.IdOne, IdTwo = m.IdTwo)}
As you can see, apart from the class, it looks the same. But when I try to use this expression in the HasKey method, I get the following InvalidOperationException:
The properties expression 'm => new ModelKey(IdOne = m.IdOne, IdTwo= m.IdTwo)'
is not valid. The expression should represent a property: C#: 't =>
t.MyProperty' VB.Net: 'Function(t) t.MyProperty'. When specifying multiple
properties use an anonymous type: C#: 't => new { t.MyProperty1,
t.MyProperty2 }' VB.Net: 'Function(t) New With { t.MyProperty1,
t.MyProperty2 }'.
So it seems to me that this anonymous class syntax does something special...
In F# 4.6, this has worked for me after much struggle.
Apperantly all you need is a tuple. Makes sense since an anon object without explicit member names is basically, a tuple.
I can't believe nobody has put this on the MS documentation tho.
modelBuilder.Entity<Entity>()
.HasKey(fun e -> (e.Key1, e.Key2) :> obj)
|> ignore
EDIT: The technique below would be needed in F# 2.0, but it should not be needed in the newer versions. There must be some other issue with the F#-generated expression tree...
I think the problem is that Entity Framework wants you to specify a lambda expression as an expression tree:
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> ModelKey (m.IdOne, m.IdTwo))
This should just work in F# 3.1 in Visual Studio 2013, but it is not supported in F# 3.0. You can still do this, but you'll have to use F# quotations and write a bit of code that converts a quotation to LINQ expression tree - there is a helper that does most of the work:
open System
open System.Linq.Expressions
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.RuntimeHelpers
let getFuncTree (quotation:Expr<'a -> 'b>) =
let e = LeafExpressionConverter.QuotationToExpression quotation
let call = e :?> MethodCallExpression
let lam = call.Arguments.[0] :?> LambdaExpression
Expression.Lambda<Func<'a, 'b>>(lam.Body, lam.Parameters)
getFuncTree <# fun x -> x + 1 #>
Using this, you should be able to call:
modelBuilder.Entity<Model>().HasKey(getFuncTree <# fun (m : Model) ->
ModelKey (m.IdOne, m.IdTwo) #>)
You could define an extension method like HasKeyQuot that does this behind the cover, to make the code a bit nicer.
It looks like the issue is twofold:
F#'s compiler never sets the Members collection of a LINQ NewExpression during quotation translation, but this is used to mark the construction of an anonymous type, which EF expects.
EF is really picky: even in C#, doing m => new { A = m.IdOne, B = m.IdTwo } won't work - the anonymous type's property names must match the model's property names.
One way to work around the issue (which is probably overkill, but works) is to create a new type dynamically at runtime which has fields with the right names and then just use a tuple in the F# code:
open Quotations.Patterns
open Quotations.ExprShape
open System.Reflection
open System.Linq.Expressions
module AnonymousTypeFixer =
let private mb =
let ab = System.AppDomain.CurrentDomain.DefineDynamicAssembly(AssemblyName("codeGen"), Emit.AssemblyBuilderAccess.ReflectionOnly)
ab.DefineDynamicModule("codeGen")
let transform (Lambda(v, (NewTuple exprs)) : Quotations.Expr<'a -> 'b>) =
let newV = Expression.Variable(v.Type, v.Name)
let cvtExpr (PropertyGet(Some(Var v'), p, [])) =
assert (v = v')
Expression.Property(newV, p) :> Expression, p
let ty = mb.DefineType(v.Type.Name)
let ctor = ty.DefineConstructor(MethodAttributes.Public (*||| MethodAttributes.RTSpecialName ||| MethodAttributes.SpecialName*), CallingConventions.HasThis, exprs |> List.map (fun e -> e.Type) |> List.toArray)
ctor.GetILGenerator().Emit(Emit.OpCodes.Ret)
let fields =
[for (_, p) in exprs |> List.map cvtExpr ->
ty.DefineField(p.Name, p.PropertyType, FieldAttributes.Public) :> MemberInfo]
ty.CreateType()
let newE = Expression.New(ctor, exprs |> Seq.map (cvtExpr >> fst), fields)
Expression.Lambda<System.Func<'a, obj>>(newE, newV)
let mb = System.Data.Entity.DbModelBuilder()
mb.Entity<Model>().HasKey(AnonymousTypeFixer.transform <# fun (m:Model) -> m.IdOne, m.IdTwo #>)
// F# 3.0
open Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter
// Regular type with properties IdOne & IdTwo.
type ModelKey (idOne, idTwo) =
member this.IdOne = idOne
member this.IdTwo = idTwo
modelBuilder.Entity<Model>()
.HasKey(QuotationToLambdaExpression(
<# Func<_,_>(fun m -> NewAnonymousObjectHelper<_>(ModelKey(m.IdOne, m.IdTwo))) #>
)
)