I wasn't thinking of any specific design choices, but rather the overall feeling of pragmatism one gets from OCaml when coming from Haskell.
Most of my favourite design choices are the ones that make OCaml a functional programming language. So mostly not apt for Go. But here's a list of what I think one could adapt while staying true to the idea of a C-like language:
C had unions. They had some obvious problems. Go's solution was to get rid of them. OCaml instead went for discriminated unions (https://www.drdobbs.com/cpp/discriminated-unions/240009296) and made the compiler check that you are handling all cases correctly. In OCaml parlance, they are called algebraic data types.
C had null pointers. Go inherited them. OCaml uses the general mechanism of discriminated unions to represent cases like 'end of a linked list' or 'no data' or 'unknown' instead. And the compiler will check for you that you either handled those cases (when declared) or that you don't create those cases (when not explicitly declared to be there).
Some of C's constructs are generic. Like accessing arrays or taking the address of a value or dereferencing a pointer. To give an example, p works for a pointer of any type and does the right thing. And the compiler will tell you off, if you mix up the types.
As a user of C you can not add your own generic operations. You can use copy-and-paste or cast to and fro void or sometimes use function pointers in a clever way.
Go goes the same route. For example the built-in datastructures are generic, like maps or arrays.
Similar to C, a user of Go can not add their own generic operations. Go's sorting https://golang.org/pkg/sort/#Interface demonstrates what I mean by clever use of function pointers. (Note, how even that sorting interface still has lots of copy-and-paste when implementing and how it only really works for in-place sorting algorithms. I'm not sure how much trouble it would give in a multi threaded environment.)
I suspect Go's aversion to user-creatable generics comes from C++. C++'s templates confuse ad-hoc polymorphism and parametric polymorphism.
If C++'s unholy mess were the only game in town, Go's stance of restricting generics to the grownups (ie language implementors) would be perfectly reasonable.
OCaml carefully separates these two kinds of polymorphism.
Parametric polymorphism basically means 'this function or data structure works identically with any type'. That's what we also call generics. Eg the length of an array doesn't depend on what's in the array. (See https://en.wikipedia.org/wiki/Parametric_polymorphism)
When compiling you only need to create code once regardless of type. (Modulo unboxed types.)
An infamous example are the bitshift operators from C whose cute looks got them roped into IO duty in C++.
In OCaml parametric polymorphism is handled as the simple concept it is. Ad hoc polymorphism (and unboxing for parametric polymorphism) are more complicated, and rightly make up the more complicated bits of OCaml types system.
Go could have gone with parametric polymorphism and left out ad-hoc polymorphism to stay simple.
(Parametric polymorphism synergises well with algebraic data types. Eg you can model the possibility of a null-pointer via an 'option' type, and have generic functionality to handle it. Same for the slightly richer concept of a value that's 'either an error message or a proper value'.)
But discussions about generics are a bit of a mine-field in Go.
Type inference. Go's type inference only goes one way, and is very limited. OCaml type inference goes backwards and forwards. Rust's system is perhaps a good compromise: it has similar machinery to OCaml, but they intentionally restrict it to only consider variable usage information from inside the same function.
(Generics restricted to parametric-polymorphism-only don't make type inference any harder.)
Back to something uncontroversial: Go allows tuples and pattern matching against them only when returning from functions and has lots of specialised machinery like 'multiple return types'.
OCaml just allows you tuples anywhere any other value could occur. You can pattern match against tuples and arbitrary other types. The compiler ensures that your pattern matching considers all cases.
I am sure there are more things to learn. But those few examples shall suffice for now. I feel especially strong about tuples as a wasted opportunity that wouldn't impact how the language feels at all. (From a practical point of view discriminated unions would probably make a bigger impact.)
Most of my favourite design choices are the ones that make OCaml a functional programming language. So mostly not apt for Go. But here's a list of what I think one could adapt while staying true to the idea of a C-like language:
C had unions. They had some obvious problems. Go's solution was to get rid of them. OCaml instead went for discriminated unions (https://www.drdobbs.com/cpp/discriminated-unions/240009296) and made the compiler check that you are handling all cases correctly. In OCaml parlance, they are called algebraic data types.
C had null pointers. Go inherited them. OCaml uses the general mechanism of discriminated unions to represent cases like 'end of a linked list' or 'no data' or 'unknown' instead. And the compiler will check for you that you either handled those cases (when declared) or that you don't create those cases (when not explicitly declared to be there).
Some of C's constructs are generic. Like accessing arrays or taking the address of a value or dereferencing a pointer. To give an example, p works for a pointer of any type and does the right thing. And the compiler will tell you off, if you mix up the types.
As a user of C you can not add your own generic operations. You can use copy-and-paste or cast to and fro void or sometimes use function pointers in a clever way.
Go goes the same route. For example the built-in datastructures are generic, like maps or arrays.
Similar to C, a user of Go can not add their own generic operations. Go's sorting https://golang.org/pkg/sort/#Interface demonstrates what I mean by clever use of function pointers. (Note, how even that sorting interface still has lots of copy-and-paste when implementing and how it only really works for in-place sorting algorithms. I'm not sure how much trouble it would give in a multi threaded environment.)
I suspect Go's aversion to user-creatable generics comes from C++. C++'s templates confuse ad-hoc polymorphism and parametric polymorphism.
If C++'s unholy mess were the only game in town, Go's stance of restricting generics to the grownups (ie language implementors) would be perfectly reasonable.
OCaml carefully separates these two kinds of polymorphism.
Parametric polymorphism basically means 'this function or data structure works identically with any type'. That's what we also call generics. Eg the length of an array doesn't depend on what's in the array. (See https://en.wikipedia.org/wiki/Parametric_polymorphism)
When compiling you only need to create code once regardless of type. (Modulo unboxed types.)
Examples of ad-hoc polymorphism (https://en.wikipedia.org/wiki/Ad_hoc_polymorphism) are things like function overloading to do different things for different data types.
An infamous example are the bitshift operators from C whose cute looks got them roped into IO duty in C++.
In OCaml parametric polymorphism is handled as the simple concept it is. Ad hoc polymorphism (and unboxing for parametric polymorphism) are more complicated, and rightly make up the more complicated bits of OCaml types system.
Go could have gone with parametric polymorphism and left out ad-hoc polymorphism to stay simple.
(Parametric polymorphism synergises well with algebraic data types. Eg you can model the possibility of a null-pointer via an 'option' type, and have generic functionality to handle it. Same for the slightly richer concept of a value that's 'either an error message or a proper value'.)
But discussions about generics are a bit of a mine-field in Go.
Type inference. Go's type inference only goes one way, and is very limited. OCaml type inference goes backwards and forwards. Rust's system is perhaps a good compromise: it has similar machinery to OCaml, but they intentionally restrict it to only consider variable usage information from inside the same function.
(Generics restricted to parametric-polymorphism-only don't make type inference any harder.)
Back to something uncontroversial: Go allows tuples and pattern matching against them only when returning from functions and has lots of specialised machinery like 'multiple return types'.
OCaml just allows you tuples anywhere any other value could occur. You can pattern match against tuples and arbitrary other types. The compiler ensures that your pattern matching considers all cases.
I am sure there are more things to learn. But those few examples shall suffice for now. I feel especially strong about tuples as a wasted opportunity that wouldn't impact how the language feels at all. (From a practical point of view discriminated unions would probably make a bigger impact.)