Possible bug in Base.List.map


#1

utop version 2.2.0 (using OCaml version 4.07.1)
Using base v0.11.1

This works fine:

Base.List.map [1;2] ~f:(fun x-> x + 1) = [2;3];;
- : bool = true

Core.List.map [1;2] ~f:(fun x-> x + 1) = [2;3];;
- : bool = true

But when using open, only Core works properly, when trying Base you get the error below:

open Core;;
List.map [1;2] ~f:(fun x-> x + 1) = [2;3];;
- : bool = true

open Base;;
List.map [1;2] ~f:(fun x-> x + 1) = [2;3];;
Error: This expression has type int list but an expression was expected of type
         int

Incorrect type inference with List.map?
The Base Module and Polymorphic Functions
#2

I think this is because Base redefines the (=) operator to be non-polymorphic, its type is int -> int -> bool


#3

Thanks for clarifying. Is there some other way to do the comparison when in open Base mode?


#4
List.equal Int.equal (List.map [1;2] ~f:(fun x -> x + 1 ))  [2;3];;

#5

This is the kind of thing that is very frustrating and irritating with “Base/Core_kernel/Core/Super_Core/Hyper_Core” from JS.

In addition to syntax errors (possibly weirdly signaled by the compiler), you can be silently exposed to a JS-flavored environment that is not what you expect.
In addition, you can also get a bunch of type errors like this if you come to introduce some code written with JS stdlib in code based on stdlib.

Of course, you can use fully qualified function names but it becomes heavyweight.

And of course, type error was enough in the simple case of@joelj to detect its cause.

Is there a way to get the status of one’s current environment (which modules are loaded, the functions and their signature) instead of #directory and #load directives?


#6

There is a specific reason for why Base shadows the polymorphic comparison operator: https://blog.janestreet.com/the-perils-of-polymorphic-compare/.


#7

From the “The perils of polymorphic compare” write-up by Yaron Minsky from 2008:

Sadly, there’s no way of convincing compare_val to use Set.equal to compare the two sets

Is it really a feature-by-design in the OCaml language to not make this possible, or would it be desirable and possible to fix this in the OCaml compiler?


#8

This is a good reason. We use int_of_string, etc. so it makes sense to go on with always being aware of the type of the object manipulated and use the dedicated function.
The problem I see is about the architecture of Base/Core_kernel/Core and its documentation combined with the mechanism of the Toplevel that is not enough explicit or in other words lets you silently make assumptions about what is in the environment.


#9

AFAIK, a static type system guarantees that an expression is well typed at compile-time. But it knows nothing about the semantics of this expression. The following compiles but makes no sense:

# let two = 1
val two : int = 1
# two * 1
- : int = 1

If you start to use the value two in your program as if it has the value of 2, the compiler can’t know that. And you will get an unexpected result (and maybe a runtime error if you give two as an argument to a function that takes only values that are -1, 0, 1 without testing its preconditions, assuming this function has neither options return type nor exception catching mechanism).
In brief, you are responsible to define semantics. And the OCaml languages offers you all necessary constructs for that.
Or nearly all, there may be limitations I’m not fully aware of, but OCaml is enough for writing extremely various and rich expressions.
Can s.o. give an example of an expression that is very hard or nearly impossible to write in OCaml?


#10

You could imagine in a world with modular implicits that you would be able to just say “(=)” and it would do the right thing. Sadly, we can’t have this now, since:

  1. Polymorphic comparison is defined in terms of the runtime representation of OCaml values, which do not contain type information.
  2. Changing the definition of polymorphic comparison would break backwards compatibility.
  3. You can have more than one module that defines functions for a given type, e.g.:
module My_set = struct
  type t = int Set.t

  let equal a b = true
end

Now, if you have two int Set.t's, and want to compare them for equality, what’s preventing the compiler from picking My_set.equal's implementation over Set.equal?


#11

@bcc32 I think does a good job of pointing out why we avoid polymorphic compare. I think it’s a pretty common view amongst experienced OCaml devs that polymorphic compare is a mistake, and they’d rather just avoid it everywhere. I agree the current deviation between Core and Base isn’t ideal, and we plan on fixing it soon, but we wanted to start Base out on the right foot, which means no polymorphic comparison by default.

Note that you can get it back if you want it, just by writing:

open Base
open Poly

#12

I see why polymorphic compare is a bad idea for modules,
but I would argue the equal operator (=) should at least work for lists and arrays of primitives,
since the construction of lists and arrays are built into the syntax of the OCaml language,
as opposed to modules such as Set.

I would be fine with a compile-time error if I’m trying to use (=) to compare two variables containing lists of some complex type of value, telling the user to please be clear about what equality function to use, but I see no reason why not to allow it for lists/arrays of primitives like ints or strings, where there cannot possibly be any ambiguity on how to compare the values.


#13

There’s no way in the language as it exists today to do what you suggest. Modular implicits will allow a form of equality that’s both principled and lightweight, but for now, the kind of special-casing you’re proposing is just not possible.

y


#14

offtop. If we abandon polymorphic comparison functions there is no sense to have memory representation values the same as it is now. Could OCaml benefit from compiling to different inner representation, for example Haskell’s Core?