{"id":2599,"date":"2020-06-25T11:49:49","date_gmt":"2020-06-25T11:49:49","guid":{"rendered":"http:\/\/dlang.org\/blog\/?p=2599"},"modified":"2021-09-30T13:30:17","modified_gmt":"2021-09-30T13:30:17","slug":"a-pattern-for-head-mutable-structures","status":"publish","type":"post","link":"https:\/\/dlang.org\/blog\/2020\/06\/25\/a-pattern-for-head-mutable-structures\/","title":{"rendered":"A Pattern for Head-mutable Structures"},"content":{"rendered":"<p><img loading=\"lazy\" src=\"http:\/\/dlang.org\/blog\/wp-content\/uploads\/2019\/03\/brain02.png\" alt=\"\" width=\"200\" height=\"200\" class=\"alignleft size-full wp-image-2025\" \/><\/p>\n<p><a href=\"http:\/\/www.informit.com\/articles\/printerfriendly\/1407357\">When Andrei Alexandrescu introduced<\/a> ranges to the <a href=\"http:\/\/dlang.org\">D programming language<\/a>, the gap between built-in and user-defined types (UDTs) narrowed, enabling new abstractions and greater composability. Even today, though, UDTs are still second-class citizens in D. One example of this is support for head mutability&#8212;the ability to manipulate a reference without changing the referenced value(s). This document details a pattern that will further narrow the UDT gap by introducing functions for defining and working with head-mutable user-defined types.<\/p>\n<h3 id=\"introduction\">Introduction<\/h3>\n<p>D is <a href=\"https:\/\/web.cs.wpi.edu\/~jshutt\/kernel.html\">neither Kernel<\/a> <a href=\"http:\/\/www.scheme-reports.org\">nor Scheme<\/a>&#8212;it has first-class and second-class citizens. Among its first-class citizens are arrays and pointers. One of the benefits these types enjoy is <a href=\"#head-mutable\">implicit conversion to head-mutable<\/a>. For instance, <code>const(T[])<\/code> is implicitly convertible to <code>const(T)[]<\/code>. Partly to address this difference, D has many ways to define how one type may convert to or behave like another &#8211; <a href=\"https:\/\/dlang.org\/spec\/class.html#alias-this\">alias this<\/a>, <a href=\"https:\/\/dlang.org\/spec\/struct.html#struct-constructor\">constructors<\/a>, <a href=\"https:\/\/tour.dlang.org\/tour\/en\/gems\/opdispatch-opapply\">opDispatch<\/a>, <a href=\"https:\/\/dlang.org\/spec\/operatoroverloading.html#cast\">opCast<\/a>, and, of course, subclassing. The way pointers and dynamic arrays decay into their head-mutable variants is different from the semantics of any of these features, so we would need to define a new type of conversion if we were to mimic this behavior.<\/p>\n<p>Changing the compiler and the language to permit yet another way of converting one type into another is not desirable: it makes the job harder for compiler writers, makes an already complex language even harder to learn, and any implicit conversion can make code harder to read and maintain. If we can define conversions to head-mutable data structures without introducing compiler or language changes, this will also make the feature available to users sooner, since such a mechanism would not necessarily require changes in the standard library, and users could gradually implement it in their own code and benefit from the code in the standard library catching up at a later point.<\/p>\n<h3 id=\"unqual\">Unqual<\/h3>\n<p>The tool used today to get a head-mutable version of a type <a href=\"https:\/\/dlang.org\/library\/std\/traits\/unqual.html\">is <code>std.traits.Unqual<\/code><\/a>. In some cases, this is the right tool&#8212;it strips away one layer of <code>const<\/code>, <code>immutable<\/code>, <code>inout<\/code>, and <code>shared<\/code>. For some types though, it either does not give a head-mutable result, or it gives a head-mutable result with mutable indirections:<\/p>\n<pre class=\"prettyprint lang-d\">struct S(T) {\r\n    T[] arr;\r\n}<\/pre>\n<p>With <code>Unqual<\/code>, this code fails to compile:<\/p>\n<pre class=\"prettyprint lang-d\">void foo(T)(T a) {\r\n    Unqual!T b = a; \/\/ cannot implicitly convert immutable(S!int) to S!int\r\n}\r\n\r\nunittest {\r\n    immutable s = S!int([1,2,3]);\r\n    foo(s);\r\n}<\/pre>\n<p>A programmer who sees that message hopefully finds a different way to achieve the same goal. However, the error message says that the conversion failed, indicating that a conversion is possible, perhaps even without issue. An inexperienced programmer, or one who knows that doing so is safe <em>right now<\/em>, could use a cast to shut the compiler up:<\/p>\n<pre class=\"prettyprint lang-d\">void bar(T)(T a) {\r\n    Unqual!T b = cast(Unqual!T)a;\r\n    b.arr[0] = 4;\r\n}\r\n\r\nunittest {\r\n    immutable s = S!int([1,2,3]);\r\n    bar(s);\r\n    assert(s.arr[0] == 1); \/\/ Fails, since bar() changed it.\r\n}<\/pre>\n<p>If, instead of <code>S!int<\/code>, the programmer had used <code>int[]<\/code>, the first example would have compiled, and the cast in the second example would have never seen the light of day. However, since <code>S!int<\/code> is a user-defined type, we are forced to write a templated function that either fails to compile for some types it really should support or gives undesirable behavior at run time.<\/p>\n<h3 id=\"headmutable\">headMutable()<\/h3>\n<p>Clearly, we should be able to do better than <code>Unqual<\/code>, and in fact we can. D has <a href=\"https:\/\/dlang.org\/spec\/template.html#TemplateThisParameter\">template this parameters<\/a> which pick up on the dynamic type of the <code>this<\/code> reference, and with that, its <code>const<\/code> or <code>immutable<\/code> status:<\/p>\n<pre class=\"prettyprint lang-d\">struct S {\r\n    void foo(this T)() {\r\n        import std.stdio : writeln;\r\n        writeln(T.stringof);\r\n    }\r\n}\r\nunittest {\r\n    S s1;\r\n    const S s2;\r\n    s1.foo(); \/\/ Prints \"S\".\r\n    s2.foo(); \/\/ Prints \"const(S)\".\r\n}<\/pre>\n<p>This way, the type has the necessary knowledge of which type qualifiers a head-mutable version needs. We can now define a method that uses this information to create the correct head-mutable type:<\/p>\n<pre class=\"prettyprint lang-d\">struct S(T) {\r\n    T[] arr;\r\n    auto headMutable(this This)() const {\r\n        import std.traits : CopyTypeQualifiers;\r\n        return S!(CopyTypeQualifiers!(This, T))(arr);\r\n    }\r\n}\r\nunittest {\r\n    const a = S!int([1,2,3]);\r\n    auto b = a.headMutable();\r\n    assert(is(typeof(b) == S!(const int))); \/\/ The correct part of the type is now const.\r\n    assert(a.arr is b.arr); \/\/ It's the same array, no copying has taken place.\r\n    b.arr[0] = 3; \/\/ Correctly fails to compile: cannot modify const expression.\r\n}<\/pre>\n<p>Thanks to the magic of <a href=\"https:\/\/tour.dlang.org\/tour\/en\/gems\/uniform-function-call-syntax-ufcs\">Uniform Function Call Syntax<\/a>, we can also define <code>headMutable()<\/code> for built-in types:<\/p>\n<pre class=\"prettyprint lang-d\">auto headMutable(T)(T value) {\r\n    import std.traits;\r\n    import std.typecons : rebindable;\r\n    static if (isPointer!T) {\r\n        \/\/ T is a pointer and decays naturally.\r\n        return value;\r\n    } else static if (isDynamicArray!T) {\r\n        \/\/ T is a dynamic array and decays naturally.\r\n        return value;\r\n    } else static if (!hasAliasing!(Unqual!T)) {\r\n        \/\/ T is a POD datatype - either a built-in type, or a struct with only POD members.\r\n        return cast(Unqual!T)value;\r\n    } else static if (is(T == class)) {\r\n        \/\/ Classes are reference types, so only the reference may be made head-mutable.\r\n        return rebindable(value);\r\n    } else static if (isAssociativeArray!T) {\r\n        \/\/ AAs are reference types, so only the reference may be made head-mutable.\r\n        return rebindable(value);\r\n    } else {\r\n        static assert(false, \"Type \"~T.stringof~\" cannot be made head-mutable.\");\r\n    }\r\n}\r\nunittest {\r\n    const(int*[3]) a = [null, null, null];\r\n    auto b = a.headMutable();\r\n    assert(is(typeof(b) == const(int)*[3]));\r\n}<\/pre>\n<p>Now, whenever we need a head-mutable variable to point to <a href=\"#tail-const\">tail-const data<\/a>, we can simply call <code>headMutable()<\/code> on the value we need to store. Unlike the ham-fisted approach of casting to <code>Unqual!T<\/code>, which may throw away important type information and also silences any error messages that may inform you of the foolishness of your actions, attempting to call <code>headMutable()<\/code> on a type that doesn&#8217;t support it will give an error message explaining what you tried to do and why it didn&#8217;t work (&#8220;Type T cannot be made head-mutable.&#8221;). The only thing missing now is a way to get the head-mutable type. Since <code>headMutable()<\/code> returns a value of that type, and is defined for all types we can convert to head-mutable, that&#8217;s a template one-liner:<\/p>\n<pre class=\"prettyprint lang-d\">import std.traits : ReturnType;\r\nalias HeadMutable(T) = ReturnType!((T t) =&gt; t.headMutable());<\/pre>\n<p>Where <code>Unqual<\/code> returns a type with potentially the wrong semantics and only gives an error once you try assigning to it, <code>HeadMutable<\/code> disallows creating the type in the first place. The programmer will have to deal with that before casting or otherwise coercing a value into the variable. Since <code>HeadMutable<\/code> uses <code>headMutable()<\/code> to figure out the type, it also gives the same informative error message when it fails.<\/p>\n<p>Lastly, since one common use case requires us to preserve the tail-const or tail-immutable properties of a type, it is beneficial to define a template that converts to head-mutable while propagating <code>const<\/code> or <code>immutable<\/code> using <a href=\"https:\/\/dlang.org\/library\/std\/traits\/copy_type_qualifiers.html\"><code>std.traits.CopyTypeQualifiers<\/code><\/a>:<\/p>\n<pre class=\"prettyprint lang-d\">import std.traits : CopyTypeQualifiers;\r\nalias HeadMutable(T, ConstSource) = HeadMutable!(CopyTypeQualifiers!(ConstSource, T));<\/pre>\n<p>This way, <code>immutable(MyStruct!int)<\/code> can become <code>MyStruct!(immutable int)<\/code>, while the const version would propagate constness instead of immutability.<\/p>\n<h3 id=\"examplecode\">Example Code<\/h3>\n<p>Since the pattern for range functions in Phobos is to have a constructor function (e.g. <code>map<\/code>) that forwards its arguments to a range type (e.g. <code>MapResult<\/code>), the code changes required to use <code>headMutable()<\/code> are rather limited. Likewise, user code should generally not need to change at all in order to use <code>headMutable()<\/code>. To give an impression of the code changes needed, I have implemented <code>map<\/code> and <code>equal<\/code>:<\/p>\n<pre class=\"prettyprint lang-d\">\r\nimport std.range;\r\n\r\n\/\/ Note that we check not if R is a range, but if HeadMutable!R is\r\nauto map(alias Fn, R)(R range) if (isInputRange!(HeadMutable!R)) {\r\n    \/\/ Using HeadMutable!R and range.headMutable() here.\r\n    \/\/ This is basically the extent to which code that uses head-mutable data types will need to change.\r\n    return MapResult!(Fn, HeadMutable!R)(range.headMutable());\r\n}\r\n\r\nstruct MapResult(alias Fn, R) {\r\n    R range;\r\n    \r\n    this(R _range) {\r\n        range = _range;\r\n    }\r\n    \r\n    void popFront() {\r\n        range.popFront();\r\n    }\r\n    \r\n    @property\r\n    auto ref front() {\r\n        return Fn(range.front);\r\n    }\r\n    \r\n    @property\r\n    bool empty() {\r\n        return range.empty;\r\n    }\r\n    \r\n    static if (isBidirectionalRange!R) {\r\n        @property\r\n        auto ref back() {\r\n            return Fn(range.back);\r\n        }\r\n\r\n        void popBack() {\r\n            range.popBack();\r\n        }\r\n    }\r\n\r\n    static if (hasLength!R) {\r\n        @property\r\n        auto length() {\r\n            return range.length;\r\n        }\r\n        alias opDollar = length;\r\n    }\r\n\r\n    static if (isRandomAccessRange!R) {\r\n        auto ref opIndex(size_t idx) {\r\n            return Fn(range[idx]);\r\n        }\r\n    }\r\n\r\n    static if (isForwardRange!R) {\r\n        @property\r\n        auto save() {\r\n            return MapResult(range.save);\r\n        }\r\n    }\r\n    \r\n    static if (hasSlicing!R) {\r\n        auto opSlice(size_t from, size_t to) {\r\n            return MapResult(range[from..to]);\r\n        }\r\n    }\r\n    \r\n    \/\/ All the above is as you would normally write it.\r\n    \/\/ We also need to implement headMutable().\r\n    \/\/ Generally, headMutable() will look very much like this - instantiate the same\r\n    \/\/ type template that defines typeof(this), use HeadMutable!(T, ConstSource) to make\r\n    \/\/ the right parts const or immutable, and call headMutable() on fields as we pass\r\n    \/\/ them to the head-mutable type.\r\n    auto headMutable(this This)() const {\r\n        alias HeadMutableMapResult = MapResult!(Fn, HeadMutable!(R, This));\r\n        return HeadMutableMapResult(range.headMutable());\r\n    }\r\n}\r\n\r\nauto equal(R1, R2)(R1 r1, R2 r2) if (isInputRange!(HeadMutable!R1) &amp;&amp; isInputRange!(HeadMutable!R2)) {\r\n    \/\/ Need to get head-mutable version of the parameters to iterate over them.\r\n    auto _r1 = r1.headMutable();\r\n    auto _r2 = r2.headMutable();\r\n    while (!_r1.empty &amp;&amp; !_r2.empty) {\r\n        if (_r1.front != _r2.front) return false;\r\n        _r1.popFront();\r\n        _r2.popFront();\r\n    }\r\n    return _r1.empty &amp;&amp; _r2.empty;\r\n}\r\n\r\nunittest {\r\n    \/\/ User code does not use headMutable at all:\r\n    const arr = [1,2,3];\r\n    const squares = arr.map!(a =&gt; a*a);\r\n    const squaresPlusTwo = squares.map!(a =&gt; a+2);\r\n    assert(equal(squaresPlusTwo, [3, 6, 11]));\r\n}<\/pre>\n<p>(Note that these implementations are <a href=\"https:\/\/github.com\/dlang\/phobos\/blob\/master\/std\/algorithm\/iteration.d#L482\">simplified slightly from Phobos code<\/a> to better showcase the use of <code>headMutable<\/code>)<\/p>\n<p>The <code>unittest<\/code> block shows a use case where the current Phobos <code>map<\/code> would fail&#8212;it is perfectly possible to create a const <code>MapResult<\/code>, but there is no way of iterating over it. Note that only two functions are impacted by the addition of <code>headMutable()<\/code>: <code>map<\/code> tests if <code>HeadMutable!R<\/code> is an input range and converts its arguments to head-mutable when passing them to <code>MapResult<\/code>, and <code>MapResult<\/code> needs to implement <code>headMutable()<\/code>. The rest of the code is exactly as you would otherwise write it.<\/p>\n<p>The implementation of <code>equal()<\/code> shows a situation where implicit conversions would be beneficial. For <code>const(int[])<\/code> the call to <code>headMutable()<\/code> is superfluous&#8212;it is implicitly converted to <code>const(int)[]<\/code> when passed to the function. For user-defined types however, this is not the case, so the call is necessary in the general case.<\/p>\n<p>While I have chosen to implement a range here, ranges are merely the most common example of a place where <code>headmutable<\/code> would be useful; the idea has merits beyond ranges. Another type in the standard library that would benefit from <code>headmutable<\/code> is <code>RefCounted!T<\/code>: <code>const(RefCounted!(T))<\/code> should convert to <code>RefCounted!(const(T))<\/code>.<\/p>\n<h3 id=\"whynottail-const\">Why not Tail-Const?<\/h3>\n<p>In previous discussions of this problem, the solution has been described <a href=\"#tail-const\">as tail-const<\/a>, and a function <code>tailConst()<\/code> has been proposed. While this idea might at first seem the most intuitive solution, it has some problems, which together make <code>headMutable()<\/code> far superior.<\/p>\n<p>The main problem with <code>tailConst()<\/code> is that it does not play well with D&#8217;s existing const system. It needs to be called on a mutable value, and there is no way to convert a <code>const(Foo!T)<\/code> to <code>Foo!(const(T))<\/code>. It thus requires that the programmer explicitly call <code>tailConst()<\/code> on any value that is to be passed to a function expecting a non-mutable value and, abstain from using <code>const<\/code> or <code>immutable<\/code> to convey the same information. This creates a separate world of tail-constness and plays havoc with generic code, which consequently has no way to guarantee that it won&#8217;t mutate its arguments.<\/p>\n<p>Secondly, the onus is placed on library users to call <code>tailConst()<\/code> whenever they pass an argument anywhere, causing an inversion of responsibility: the user has to tell the library that it is not allowed to edit the data instead of the other way around. In the best case, this merely causes unnecessary verbiage. In other cases, the omission of <code>const<\/code> will lead to mutation of data expected to be immutable.<\/p>\n<p>A minor quibble in comparison is that the tail-const solution also requires the existence of <code>tailImmutable<\/code> to cover the cases where the values are immutable.<\/p>\n<h2 id=\"issues\">Issues<\/h2>\n<p>The ideas outlined in this document concern only conversion to head-mutable. A related issue is conversion to tail const, e.g. from <code>RefCounted!T<\/code> or <code>RefCounted!(immutable T)<\/code> to <code>RefCounted!(const T)<\/code>, a conversion that, again, is implicit for arrays and pointers in D today.<\/p>\n<p>One issue that may be serious is the fact that <code>headMutable<\/code> often cannot be <code>@safe<\/code> and may, in fact, need to rely on undefined behavior in some places. For instance, <code>RefCounted!T<\/code> contains a pointer to the actual ref count. For <code>immutable(RefCounted!T)<\/code>, <code>headMutable()<\/code> would need to cast away immutable, which <a href=\"https:\/\/dlang.org\/spec\/const3.html#removing_with_cast\">is undefined behavior per the spec<\/a>.<\/p>\n<h3 id=\"thecompilersolution\">The Compiler Solution<\/h3>\n<p>It is logical to think that, as with built-in types, <code>headMutable()<\/code> could be elided in its entirety, and the compiler could handle the conversions for us. In many cases, this would be possible, and in fact the compiler already does so for POD types like <code>struct S { int n; }<\/code>&#8212;a <code>const<\/code> or <code>immutable<\/code> <code>S<\/code> may be assigned to a mutable variable of type <code>S<\/code>. This breaks down, however, when the type includes some level of mutable indirection. For templated types it would be possible to wiggle the template parameters to see if the resulting type compiles and has fields with the same offsets and similar types, but even such an intelligent solution breaks down in the presence of D&#8217;s Turing-complete template system, and some cases will always need to be handled by the implementer of a type.<\/p>\n<p>It is also a virtue that the logic behind such an implementation be understandable to the average D programmer. The best case result of that not being true is that the forums would be inundated with a flood of posts about why types don&#8217;t convert the way users expect them to.<\/p>\n<p>For these reasons, <code>headMutable()<\/code> will be necessary even with compiler support. But what would that support look like? Implicit casting to head-mutable happens in the language today in two situations:<\/p>\n<ul>\n<li>Assignment to head-mutable variables: <code>const(int)[] a = create!(const(int[]))();<\/code> (all POD types, pointers and arrays)<\/li>\n<li>Function calls: <code>fun(create!(const(int[]))();<\/code> (only pointers and arrays)<\/li>\n<\/ul>\n<p>The first is covered by existing language features (<code>alias headMutable this;<\/code> fits the bill perfectly). The second is not but is equivalent to calling <code>.headMutable<\/code> whenever a <code>const<\/code> or <code>immutable<\/code> value is passed to a function that does not explicitly expect a <code>const<\/code> or <code>immutable<\/code> argument. This would change the behavior of existing code, in that templated functions would prefer <code>a.headMutable<\/code> over <code>a<\/code>, but would greatly improve the experience of working with <code>const<\/code> types that do define <code>headMutable()<\/code>. If <code>headMutable<\/code> is correctly implemented, the different choice of template instantiations should not cause any actual breakage.<\/p>\n<h3 id=\"futurework\">Future Work<\/h3>\n<p>While this document proposes to implement the described feature without any changes to the compiler or language, it would be possible for the compiler in the future to recognize <code>headMutable()<\/code> and call it whenever a type that defines that method is passed to a function that doesn&#8217;t explicitly take exactly that type, or upon assignment to a variable that matches <code>headMutable()<\/code>&#8217;s return value. This behavior would mirror the current behavior of pointers and arrays.<\/p>\n<h3 id=\"conclusion\">Conclusion<\/h3>\n<p>It is possible to create a framework for defining head-mutable types in D today without compiler or language changes. It requires a little more code in the methods that use head-mutable types but offers a solution to a problem that has bothered the D community for a long time.<\/p>\n<p>While this document deals mostly with ranges, other types will also benefit from this pattern: smart pointers and mutable graphs with immutable nodes are but two possible examples.<\/p>\n<h3 id=\"definitions\">Definitions<\/h3>\n<h5 id=\"head-mutable\">Head-mutable<\/h5>\n<p>A type is head-mutable if some or all of its members without indirections are mutable. Note that a head-mutable datatype may also have <code>const<\/code> or <code>immutable<\/code> members without indirections; the requirement is merely that some subset of its members may be mutated. A head-mutable datatype may be tail-const, tail-immutable or tail-mutable&#8212;head-mutable only refers to its non-indirected members. Examples of head-mutable types include <code>const(int)[]<\/code>, <code>int*<\/code>, <code>string<\/code>, and <code>Rebindable!MyClass<\/code>. Types without indirections (like <code>int<\/code>, <code>float<\/code> and <code>struct S { int n; }<\/code>) are trivially head-mutable.<\/p>\n<h5 id=\"tail-const\">Tail-const<\/h5>\n<p>A type is tail-const if some of its members with indirections have the <code>const<\/code> type qualifier. A tail-const type may be head-mutable or head-const. Examples of tail-const types are <code>const(int)*<\/code>, <code>const(int[])<\/code>, <code>const(immutable(int)[])*<\/code> and <code>string<\/code>.<\/p>\n<h3 id=\"source\">Source<\/h3>\n<p>The source code for <code>HeadMutable<\/code> and <code>headMutable<\/code> <a href=\"https:\/\/gist.github.com\/Biotronic\/67bebfe97f17e73cc610d9bcd119adfb\">is available here<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When Andrei Alexandrescu introduced ranges to the D programming language, the gap between built-in and user-defined types (UDTs) narrowed, enabling new abstractions and greater composability. Even today, though, UDTs are still second-class citizens in D. One example of this is support for head mutability&#8212;the ability to manipulate a reference without changing the referenced value(s). This [&hellip;]<\/p>\n","protected":false},"author":41,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[26,9,10,20],"tags":[],"_links":{"self":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/2599"}],"collection":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/comments?post=2599"}],"version-history":[{"count":4,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/2599\/revisions"}],"predecessor-version":[{"id":2603,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/2599\/revisions\/2603"}],"wp:attachment":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/media?parent=2599"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/categories?post=2599"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/tags?post=2599"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}