int * int vs (int * int) in OCaml sum type

后端 未结 4 761
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-19 01:15
type foo = A of int * int | B of (int * int)

What is the difference between int * int and (int * int) there? The only dif

相关标签:
4条回答
  • They are two different types. The interpretation of this syntax is ambiguous at the * operator. It may be reduced into the form:

    type x = Y * Z in which the '*' is associated with the type keyword in OCaml or int * int in which the * is used in the capacity of an operator that constructs a tuple

    The default precedence takes it to the former. By putting a parenthesis around the (int * int) you override the default precedence and force the latter interpretation.

    0 讨论(0)
  • 2020-12-19 02:14

    This is one of the tricky things in OCaml syntax -- even though it looks like you are declaring a constructor with a tuple data type (A of int * int), and even though when you use the constructor, it looks like you are giving a tuple to it (A (2,3)), that is not actually what is happening.

    If you actually construct a tuple value and try to pass it to the constructor, it will not compile -- let x = (2,3) in A x. Rather, the * in the constructor definition and the (,) in the constructor use expression are simply the syntax for a constructor of multiple arguments. The syntax imitates that of a constructor with a tuple argument, but is actually separate. The extra parentheses are necessary if you want to actually make a constructor with a single tuple argument.

    0 讨论(0)
  • 2020-12-19 02:17

    Yes, there is a performance difference:

    In memory A (23, 42) will contain a tag identifying it as an A and the two integers 23 and 42. B (23, 42) will contain a tag identifying it as a B and a pointer to a tuple containing the integers 23 and 42. So there will be one additional memory allocation when creating a B and one additional level of indirection when accessing the individual values inside a B. So in cases where you don't actually use the constructor arguments as a tuple, using A will involve less overhead than using B.

    On the other hand your test_foo function will create a new tuple every time it is called with an A value, but when it is called with a B value it will simply return the tuple that already exists in memory. So test_foo is a cheaper operation for B than it is for A. So if you'll be using the constructor's arguments as a tuple and you will do so multiple times for the same value, using B will be cheaper.

    So if you're going to be using the constructor arguments as a tuple, it makes sense to use a constructor taking a tuple because you can get at the tuple using pattern matching with less code and because it will avoid having to create tuples from the same value multiple times. In all other cases not using a tuple is preferable because it involves less memory allocation and less indirection.

    0 讨论(0)
  • 2020-12-19 02:18

    As already said, the constructor of A takes two int, whereas the constructor of B takes an ordered pair.

    so you can write

    let bar = A (1, 2)
    

    or

    let bar = B (1, 2)
    

    or

    let bar = (1, 2)
    let baz = B bar
    

    but you cannot write

    let bar = (1, 2)
    let baz = A bar
    

    Moreover, in your pattern matching, you can still match the content of B as two int, but you cannot match the content of A as value bound to an ordered pair

    let test_foo = function
      | A a -> a (* wrong *)
      | B (f, s) -> (f, s) (* ok *)
    
    0 讨论(0)
提交回复
热议问题