Does Standard ML validate (CBV) eta equivalence? $\eta$ equality of functions is fundamental in their Category-theoretic semantics but in practice even "functional" languages include "impure" features that violate it.
Note that this is not an issue of CBN vs CBV, in CBN eta equality $M = \lambda x. M x$ should hold for any term $M : A \to B$ whereas in CBV this will be restricted to values $V = \lambda x. V x$. The CBV $\eta$ equality has a perfectly good category-theoretic explanation, see Levy's Call-by-push-value.


Haskell includes seq, which can distinguish between (\x -&gt; error "") and error ""
OCaml includes operations like "physical equality" (==), and which will distinguish fun x =&gt; x from fun y =&gt; (fun x =&gt; x) y because == corresponds to pointer equality.


So I wonder do any "real" languages satisfy eta for observational equivalence at all?
All the better if it is known that a (correct) compiler implementation performs eta reductions just based on the type.

The only example I know of is Coq which includes it as a definitional equality as of a recent version. Maybe similar languages (Agda, Idris) do as well. These are not surprising because they include an equality type and it is very frustrating to prove equality of functions without $\eta$ or the stronger(?) extensionality principle.

My main question is about Standard ML specifically which I think does not have pointer equality for functions. Does it satisfy the CBV $\eta$ law? Furthermore do compilers for SML such as MLton use $\eta$ when optimizing programs?
@xuq01's guess is (extremely surprisingly!) wrong. The CBV eta rule described in the question is sound in Standard ML: the value v is contextually equivalent to fn x =&gt; v x. 

There are two caveats to this.


First, in specific implementations this equivalence might not be sound: in SML/NJ, you could use Unsafe.cast to detect physical equality of function values. However, unsafe features are not part of the standard. 
Second, eta with functions and sums interacts in a slightly subtle way, due to clausal definitions and incomplete pattern matching. In particular, the definitions:

fun f (SOME x) (SOME y) = x + y


and

val g = fn (SOME x) =&gt; (fn (SOME y) =&gt; x + y)


are not contextually equivalent. This is because the call f NONE will return a function, and g NONE will raise an error. 

- f NONE;
val it = fn : int option -&gt; int

- g NONE;
uncaught exception Match [nonexhaustive match failure]
raised at: stdIn:2.32


The reason this happens is operationally obvious, and of course a call-by-push-value/polarization setup makes this choice explicable. But if you really want to seriously understand clausal definitions, you are led to the idea that clausal definitions should be primary, and the usual term formers should be derived notions. See Abel, Pientka, Thibodeau and Setzer's Copatterns: programming infinite structures by observations for an example of a polarized calculus in this style. (See also Paul Levy's Jumbo Lambda-Calculus, for another calculus based on this observation.)