29

Language` context is around for a long time. It is also heavily used (APIFunctions / FormFunctions dependencies deployment rely on it).

It is undocumented and while I understand WRI won't support it just because we know how to use it, it will be very handy to have clear usage descriptions of:

? Language`*

enter image description here

Feel free to add/edit exsiting answers, they are based on our experience rather than internal knowledge so they may not be accurate.

Index:

  • `*Definition*

    • `DefinitionList
    • `ExtendedDefinition
    • `ExtendedFullDefinition
  • `*Mutation*

    • `SetMutationHandler
    • `HasMutationHandlerQ
    • `HandleMutation
    • `MutationFallthrough
  • `*ExpressionStore*

    • `NewExpressionStore
    • `ExpressionStore

TODO:

{ `ArrayObject, `DestructureLValue, `FromArrayObject
, `FromVariableLengthInteger,  `FullGet, `GetLValueSymbol
, `InactivateExclusionsDefault, `LinkCombine, `NewArrayObject
, `PropertyItemMap, `TimeVariableLengthInteger
, `ToVariableLengthInteger, `UpdateInactivateExclusions
}
Kuba
  • 136,707
  • 13
  • 279
  • 740
  • To be clear, is the question "what do the functions in Language`* do"? – QuantumDot Feb 14 '18 at 16:22
  • 1
    @quantumdot yes – Kuba Feb 14 '18 at 16:26
  • Will a more ambitious project be possible to document all the undocumented functions? – Αλέξανδρος Ζεγγ Sep 26 '18 at 05:07
  • 1
    @ΑλέξανδροςΖεγγ It would be hard to find motivation knowing that the answer is somewhere at WRIs desks anyway. So we end up cracking stuff we need or find interesting. You can always start such project, maybe on github because it may be too broad here. – Kuba Sep 26 '18 at 06:33
  • This question is a bit version-dependent. For example, in v12 there are no symbols called ArrayObject, NewArrayObject, and FromArrayObject, because they are replaced with the official and documented NumericArray and Normal@NumericArray. – Ray Shadow May 07 '19 at 20:51
  • 1
    Language`*VariableLengthInteger functions allow one to convert an arbitrary integer to a short byte sequence and vice versa. This encoding method is used in WXF file format and described in the section Length Encoding (Varint) of https://reference.wolfram.com/language/tutorial/WXFFormatDescription.html – Ray Shadow May 07 '19 at 21:05

3 Answers3

20

Language`*Mutation*

First seen here, implemented by Taliesin Beynon

What do they do?

The only functions I really know about in this bunch are the ?Language`*Mutation* functions.

This is an interface that allows you to build things that work like CloudExpression. For example, I can make a CloudExpression and use AppendTo on it:

ce = CreateCloudExpression[<||>]

(* ==> CloudExpression[...]*)

AppendTo[ce, 1 -> 2]

(* ==> CloudExpression[...] *)

This is really implemented at the UpValues level, but for many things the expression itself might be too deep to use via UpValues, e.g.:

ce[[1]] = 4

(* ==> 4*)

For this they use Language`SetMutationHandler to handle what would otherwise have been a failed assignment:

obj["uuid"][[1]] = 2

(* > Set::setps: obj[uuid] in the part assignment is not a symbol. *)

(* ==> 2*)

And there are a bunch of symbols these days that use this:

ToExpression[
  Names["*`*"]
, StandardForm
, Function[Null, If[Language`HasMutationHandlerQ[#], #, Nothing], HoldFirst]
]

({Audio`AudioGraph, AudioStream, CloudExpression, EntityStore, InitializationValue, PersistentObject, PersistentValue})

Language`SetMutationHandler

This is the heart of the interface. We call it like:

Language`SetMutationHandler[type, handler]

which registers a mutation handler for type. CloudExpression for instance has the handler CloudExpression`PackageScope`CloudExpressionMutate.

Here's a concrete example of this in action:

oop`mut~SetAttributes~HoldAllComplete
oop`mut[Set[f_[oop`ob[s_], p___], v_]] := Set[f[s, p], v]

sym = {1, 2}; oop`ob[sym][[1]] = 2 sym

(Set::setps: oop`ob[sym] in the part assignment is not a symbol.)

(* ==> 2 ) ( ==> {1, 2} *)

LanguageSetMutationHandler[oopob, oop`mut]

oop`ob[sym][[1]] = 2 sym

(* ==> 2 ) ( ==> {2, 2} *)

Language`HasMutationHandlerQ

Pretty clear from the previous section

Language`HandleMutation

This is how the MutationHandler system does its dirty work. For example:

Language`HandleMutation[oop`ob[sym][[1]] = 2]

(* Set::noval: Symbol sym in part assignment does not have an immediate value. *)

(* ==> HoldComplete[1] *)

One thing to note, if the Handler doesn't do anything, the system will keep trying to mutate it until it bottoms out:

Language`SetMutationHandler[oop`ob, oop`mut2]
Language`HandleMutation[oop`ob[sym][[1]] = 2]

(* oop`ob::mutreclim: Too many nested mutations occurred. *)

(* oopob::modfl: The modification oopob[sym][[1]]=2 could not be performed. *)

(* ==> $Failed *)

Language`MutationFallthrough

Language`MutationFallthrough seems to tell the system to abort the mutation process and raise the most recent failed result, for instance this total failure:

oop`mut3~SetAttributes~HoldAllComplete
Language`SetMutationHandler[oop`ob, oop`mut3]
Language`HandleMutation[oop`ob[sym][[1]] = 2]

(* oopob::modfl: The modification oopob[sym][[1]]=2 could not be performed. *)

(* ==> $Failed *)

is transmogrified into this basic failure we'd expect without the mutation system when using Language`MutationFallthrough:

oop`mut3[___] := Language`MutationFallthrough
Language`HandleMutation[oop`ob[sym][[1]] = 2]

(* Set::setps: oop`ob[sym] in the part assignment is not a symbol. *)

(* ==> HoldComplete[2] *)

daneelsan
  • 348
  • 1
  • 6
b3m2a1
  • 46,870
  • 3
  • 92
  • 239
  • So can we say that the mutation handling is a general way to handle modifying complex data types without actually introducing entirely new data types (i.e. overloading all functions like AppendTo to handle the new head)? – István Zachar Feb 15 '18 at 10:07
  • 1
    Sort-of. Your mutation handler function will still need to define a way to handle things like AppendTo. Rather I'd say it moves the work from UpValues to DownValues in mutating a data-type, allowing you to escape, e.g., the depth one restriction without sacrificing performance. – b3m2a1 Feb 15 '18 at 10:12
  • What is the point of Set[f[s], p] as opposed to Set[f[s,p], v]? in SetMutationHandler example. 2) That may be subjective but I find it terrible to read with all those contexts and short names. What do you think about: ObjectMutation[ part_[Object[s_], spec___] = value_ ] := Set[part[s, spec], value], unless I missed the point or you disagree I could edit the post since I'm trying to parse it anyway.
  • – Kuba Nov 12 '18 at 12:27