Create an object/Native demonstration: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|zkl}}: fix syntax highlighting markup)
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(5 intermediate revisions by 4 users not shown)
Line 10: Line 10:
If the language supports '''Magic Methods''', then show how these work.
If the language supports '''Magic Methods''', then show how these work.


=={{header|BASIC256}}==
=={{header|BASIC}}==
<lang BASIC256>map mapa
==={{header|BASIC256}}===
<syntaxhighlight lang="basic256">map mapa
mapa["A"] = 65
mapa["A"] = 65
mapa["B"] = 66
mapa["B"] = 66
Line 19: Line 20:
print valor
print valor
print mapa[valor]
print mapa[valor]
next valor</lang>
next valor</syntaxhighlight>
{{out}}
{{out}}
<pre>A
<pre>A
Line 26: Line 27:
66
66
C
C
6</pre>
67</pre>

==={{header|FreeBASIC}}===
FB doesn't have Dict natively, but we can implement them via Type
<syntaxhighlight lang="freebasic">Type dict
m1 As String*1
m2 As Integer
End Type

Dim mapOf(1 To 3) As dict => {("A", 65), ("B", 66), ("C", 67)}

For i As Integer = 1 To Ubound(mapOf)
Print mapOf(i).m1
Print mapOf(i).m2
Next i</syntaxhighlight>
{{out}}
<pre>A
65
B
66
C
67</pre>


=={{header|C++}}==
=={{header|C++}}==
<lang cpp>#include <iostream>
<syntaxhighlight lang="cpp">#include <iostream>
#include <map>
#include <map>
#include <utility>
#include <utility>
Line 133: Line 155:
}
}
}
}
</syntaxhighlight>
</lang>
{{out}}
{{out}}
<pre>Map intialized with values
<pre>Map intialized with values
Line 155: Line 177:
=={{header|D}}==
=={{header|D}}==
{{trans|Python}}
{{trans|Python}}
<lang d>struct DefaultAA(TK, TV) {
<syntaxhighlight lang="d">struct DefaultAA(TK, TV) {
TV[TK] standard, current;
TV[TK] standard, current;


Line 187: Line 209:
d.remove("a");
d.remove("a");
d.writeln; // ["a":1, "b":66]
d.writeln; // ["a":1, "b":66]
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>["a":1, "b":2]
<pre>["a":1, "b":2]
Line 199: Line 221:


First create a sub-directory, romap, of the project directory and place the following package in it:
First create a sub-directory, romap, of the project directory and place the following package in it:
<lang go>package romap
<syntaxhighlight lang="go">package romap


type Romap struct{ imap map[byte]int }
type Romap struct{ imap map[byte]int }
Line 223: Line 245:
rom.imap[key] = 0 // default value of int
rom.imap[key] = 0 // default value of int
}
}
}</lang>
}</syntaxhighlight>


This package can now be imported and used within the main package as follows:
This package can now be imported and used within the main package as follows:
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
Line 248: Line 270:
i, _ = rom.Get('C')
i, _ = rom.Get('C')
fmt.Println("'C' now maps to", i)
fmt.Println("'C' now maps to", i)
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 260: Line 282:
Given a list of keys and an associated list of values, the idiomatic way of expressing this concept in J would be:
Given a list of keys and an associated list of values, the idiomatic way of expressing this concept in J would be:


<lang j>lookup=: values {~ keys&i.</lang>
<syntaxhighlight lang="j">lookup=: values {~ keys&i.</syntaxhighlight>


For example:
For example:


<lang j> lookup=: 10 20 30 40 50 {~ (;:'this is a test')&i.
<syntaxhighlight lang="j"> lookup=: 10 20 30 40 50 {~ (;:'this is a test')&i.
lookup ;:'a test'
lookup ;:'a test'
30 40</lang>
30 40</syntaxhighlight>


Notes:
Notes:
Line 280: Line 302:
Java supports unmodifiable maps, sets, lists, and other more specialized unmodifiable collections. In this example, we have a unmodifiable map. We first create an ordinary map, modify as needed, then call the <code>Collections.unmodifiableMap</code>. We can subsequently read the map, but modification is not permitted. The returned map will subsequently throw a <code>UnsupportedOperationException</code> exception if a mutation operator is called. Several are demonstrated below.
Java supports unmodifiable maps, sets, lists, and other more specialized unmodifiable collections. In this example, we have a unmodifiable map. We first create an ordinary map, modify as needed, then call the <code>Collections.unmodifiableMap</code>. We can subsequently read the map, but modification is not permitted. The returned map will subsequently throw a <code>UnsupportedOperationException</code> exception if a mutation operator is called. Several are demonstrated below.


<lang java>
<syntaxhighlight lang="java">
import java.util.Collections;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashMap;
Line 325: Line 347:
}
}
</syntaxhighlight>
</lang>


{out}}
{out}}
Line 342: Line 364:
{{works with|JavaScript|1.7}}
{{works with|JavaScript|1.7}}


<lang javascript>var keyError = new Error("Invalid Key Error (FixedKeyDict)") ;
<syntaxhighlight lang="javascript">var keyError = new Error("Invalid Key Error (FixedKeyDict)") ;


function FixedKeyDict(obj)
function FixedKeyDict(obj)
Line 404: Line 426:
return "FixedKeyDict{" + s + "}" ;
return "FixedKeyDict{" + s + "}" ;
} ;
} ;
}</lang>
}</syntaxhighlight>


Test run:
Test run:


<lang javascript>
<syntaxhighlight lang="javascript">
const BR = "<BR>\n"
const BR = "<BR>\n"


Line 442: Line 464:
pl("error test : " + e.message) ;
pl("error test : " + e.message) ;
}
}
</syntaxhighlight>
</lang>


output :
output :
Line 465: Line 487:


=={{header|jq}}==
=={{header|jq}}==
jq objects are JSON objects and can be created using JSON syntax, e.g. <lang jq>{"language": "jq"}</lang>
jq objects are JSON objects and can be created using JSON syntax, e.g. <syntaxhighlight lang="jq">{"language": "jq"}</syntaxhighlight>
Objects can also be created programmatically, e.g. <lang jq>{"one": 1} + {"two": 2}</lang>
Objects can also be created programmatically, e.g. <syntaxhighlight lang="jq">{"one": 1} + {"two": 2}</syntaxhighlight>


jq objects, however, are really just values: they are immutable, and cannot be "deleted" any more than the number 1 can be deleted.
jq objects, however, are really just values: they are immutable, and cannot be "deleted" any more than the number 1 can be deleted.


=={{header|Julia}}==
=={{header|Julia}}==
<syntaxhighlight lang="julia">
<lang Julia>
using BackedUpImmutable
using BackedUpImmutable


Line 497: Line 519:
@test fibr["a"] == 0
@test fibr["a"] == 0
end
end
</syntaxhighlight>
</lang>
All tests pass.
All tests pass.


=={{header|Kotlin}}==
=={{header|Kotlin}}==
<lang scala>// version 1.1.2
<syntaxhighlight lang="scala">// version 1.1.2


fun main(args: Array<String>) {
fun main(args: Array<String>) {
Line 507: Line 529:
val map = mapOf('A' to 65, 'B' to 66, 'C' to 67)
val map = mapOf('A' to 65, 'B' to 66, 'C' to 67)
println(map)
println(map)
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 516: Line 538:
=={{header|M2000 Interpreter}}==
=={{header|M2000 Interpreter}}==
{{trans|C sharp}}
{{trans|C sharp}}
<syntaxhighlight lang="m2000 interpreter">
<lang M2000 Interpreter>
Module CheckIt {
Module CheckIt {
Class LockedHash {
Class LockedHash {
Line 586: Line 608:
}
}
Checkit
Checkit
</syntaxhighlight>
</lang>


=={{header|Mathematica}} / {{header|Wolfram Language}}==
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<lang Mathematica>a[1] = "Do not modify after creation";
<syntaxhighlight lang="mathematica">a[1] = "Do not modify after creation";
a[2] = "Native demonstration";
a[2] = "Native demonstration";
Protect[a];</lang>
Protect[a];</syntaxhighlight>
Example usage:
Example usage:
<pre>a[3] = 2
<pre>a[3] = 2
Line 599: Line 621:
=={{header|Nim}}==
=={{header|Nim}}==
We leverage native stdlib table as our own object by implementing limited actual native table functionalities.
We leverage native stdlib table as our own object by implementing limited actual native table functionalities.
<lang nim>import tables, options
<syntaxhighlight lang="nim">import tables, options


type
type
Line 640: Line 662:


main()
main()
</syntaxhighlight>
</lang>
{{output}}
{{output}}
<pre>
<pre>
Line 647: Line 669:


=={{header|Perl}}==
=={{header|Perl}}==
<syntaxhighlight lang="perl">use strict;
<lang perl>package LockedHash;

use parent Tie::Hash;
package LockedHash;
use parent 'Tie::Hash';
use Carp;
use Carp;
use strict;


sub TIEHASH {
sub TIEHASH {
Line 690: Line 713:
}
}


sub lock_hash(\%) {
sub lock_hash :prototype(\%) {
my $ref = shift;
my $ref = shift;
tie(%$ref, __PACKAGE__, %$ref);
tie(%$ref, __PACKAGE__, %$ref);
Line 715: Line 738:
# add a new key x: will die
# add a new key x: will die
eval { $h{x} = 1 };
eval { $h{x} = 1 };
if ($@) { print "Operation error: $@" }</lang>output:<lang>a => 3
if ($@) { print "Operation error: $@" }</syntaxhighlight>output:<syntaxhighlight lang="text">a => 3
b => 4
b => 4
c => 5
c => 5
Line 724: Line 747:
operation error: Can't add key x at test.pl line 14
operation error: Can't add key x at test.pl line 14
LockedHash::STORE('LockedHash=HASH(0x8cebe14)', 'x', 1) called at test.pl line 66
LockedHash::STORE('LockedHash=HASH(0x8cebe14)', 'x', 1) called at test.pl line 66
eval {...} called at test.pl line 66</lang>
eval {...} called at test.pl line 66</syntaxhighlight>


=={{header|Phix}}==
=={{header|Phix}}==
There is no native "read-only" setting on phix dictionaries, so the following wraps a pair of them to
There is no native "read-only" setting on phix dictionaries, so the following wraps a pair of them to
provide the requested functionality.
provide the requested functionality.
<!--<lang Phix>(phixonline)-->
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">enum</span> <span style="color: #000000;">STD</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">CUR</span>
<span style="color: #008080;">enum</span> <span style="color: #000000;">STD</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">CUR</span>
Line 811: Line 834:
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span>
<!--</lang>-->
<!--</syntaxhighlight>-->


=={{header|Python}}==
=={{header|Python}}==
<lang python>
<syntaxhighlight lang="python">
from collections import UserDict
from collections import UserDict
import copy
import copy
Line 901: Line 924:
raise KeyError
raise KeyError
else:
else:
return super().setdefault(key, default)</lang>
return super().setdefault(key, default)</syntaxhighlight>


=={{header|Racket}}==
=={{header|Racket}}==
Line 908: Line 931:


Implementation of functions that handle fenced-hash:
Implementation of functions that handle fenced-hash:
<syntaxhighlight lang="racket">
<lang Racket>
;(struct fenced-hash (actual original) ...)
;(struct fenced-hash (actual original) ...)


Line 946: Line 969:
;private custom-write ;mode is ignored
;private custom-write ;mode is ignored
(write-string "#fenced-hash" port)
(write-string "#fenced-hash" port)
(write (hash->list (fenced-hash-actual dict)) port))</lang>
(write (hash->list (fenced-hash-actual dict)) port))</syntaxhighlight>


Definition of the actual structure and a “public” creator:
Definition of the actual structure and a “public” creator:
<lang Racket>(struct fenced-hash (actual original)
<syntaxhighlight lang="racket">(struct fenced-hash (actual original)
#:extra-constructor-name *fenced-hash ;private constructor
#:extra-constructor-name *fenced-hash ;private constructor
#:omit-define-syntaxes ;not sure this is a good idea
#:omit-define-syntaxes ;not sure this is a good idea
Line 969: Line 992:
(define (fenced-hash . args) ; public constructor
(define (fenced-hash . args) ; public constructor
(define original (apply hash args))
(define original (apply hash args))
(*fenced-hash (hash-copy original) original))</lang>
(*fenced-hash (hash-copy original) original))</syntaxhighlight>


'''Example:''' Use the fenced-hash functions:
'''Example:''' Use the fenced-hash functions:
<lang Racket>(define d (fenced-hash "a" 1 "b" 2))
<syntaxhighlight lang="racket">(define d (fenced-hash "a" 1 "b" 2))


(displayln d)
(displayln d)
Line 984: Line 1,007:
(displayln d)
(displayln d)
(fenced-hash-remove! d "a")
(fenced-hash-remove! d "a")
(displayln d)</lang>
(displayln d)</syntaxhighlight>
{{out}}
{{out}}
<pre>#fenced-hash(("b" . 2) ("a" . 1))
<pre>#fenced-hash(("b" . 2) ("a" . 1))
Line 993: Line 1,016:


'''Example (continued):''' Use the same object as a dict. The dict-clear! method is not defined, so we must call fenced-hash-clear! instead.
'''Example (continued):''' Use the same object as a dict. The dict-clear! method is not defined, so we must call fenced-hash-clear! instead.
<lang Racket>(fenced-hash-clear! d)
<syntaxhighlight lang="racket">(fenced-hash-clear! d)
(displayln d)
(displayln d)
(dict-set! d "a" 55)
(dict-set! d "a" 55)
Line 1,004: Line 1,027:
(displayln d)
(displayln d)
(dict-remove! d "a")
(dict-remove! d "a")
(displayln d)</lang>
(displayln d)</syntaxhighlight>
{{out}}
{{out}}
<pre>#fenced-hash(("b" . 2) ("a" . 1))
<pre>#fenced-hash(("b" . 2) ("a" . 1))
Line 1,016: Line 1,039:
{{Works with|rakudo|2016.08}}
{{Works with|rakudo|2016.08}}
Here we use delegation to handle all the normal hash methods that we don't need to override to define our new class.
Here we use delegation to handle all the normal hash methods that we don't need to override to define our new class.
<lang perl6>class FixedHash {
<syntaxhighlight lang="raku" line>class FixedHash {
has $.hash handles *;
has $.hash handles *;
method new(*@args) { self.bless: hash => Hash.new: @args }
method new(*@args) { self.bless: hash => Hash.new: @args }
Line 1,034: Line 1,057:
say $fh<c>; # Nil
say $fh<c>; # Nil
$fh<c> = 43; # error
$fh<c> = 43; # error
</syntaxhighlight>
</lang>
{{out}}
{{out}}
<pre>(1 2)
<pre>(1 2)
Line 1,047: Line 1,070:
By defining [http://design.raku.org/S12.html#FALLBACK_methods FALLBACK] any class can handle undefined method calls. Since any class inherits plenty of methods from <tt>Any</tt> our magic object will be more of a novice conjurer then a master wizard proper.
By defining [http://design.raku.org/S12.html#FALLBACK_methods FALLBACK] any class can handle undefined method calls. Since any class inherits plenty of methods from <tt>Any</tt> our magic object will be more of a novice conjurer then a master wizard proper.


<lang perl6>class Magic {
<syntaxhighlight lang="raku" line>class Magic {
has %.hash;
has %.hash;
multi method FALLBACK($name, |c) is rw { # this will eat any extra parameters
multi method FALLBACK($name, |c) is rw { # this will eat any extra parameters
Line 1,061: Line 1,084:
$magic.foo = 10;
$magic.foo = 10;
say $magic.foo;
say $magic.foo;
$magic.defined = False; # error</lang>
$magic.defined = False; # error</syntaxhighlight>


{{output}}
{{output}}
Line 1,069: Line 1,092:


=={{header|Ring}}==
=={{header|Ring}}==
<lang ring>
<syntaxhighlight lang="ring">
# Project : Create an object/Native demonstration
# Project : Create an object/Native demonstration


Line 1,077: Line 1,100:
map["C"] = 67
map["C"] = 67
see map + nl
see map + nl
</syntaxhighlight>
</lang>
Output:
Output:
<pre>
<pre>
Line 1,090: Line 1,113:
=={{header|Ruby}}==
=={{header|Ruby}}==
{{works with|Ruby|1.9}}
{{works with|Ruby|1.9}}
<lang ruby># A FencedHash acts like a Hash, but with a fence around its keys.
<syntaxhighlight lang="ruby"># A FencedHash acts like a Hash, but with a fence around its keys.
# One may change its values, but not its keys. Any attempt to insert
# One may change its values, but not its keys. Any attempt to insert
# a new key raises KeyError. One may delete a key, but this only
# a new key raises KeyError. One may delete a key, but this only
Line 1,333: Line 1,356:
keys.map {|key| self[key]}
keys.map {|key| self[key]}
end
end
end</lang>
end</syntaxhighlight>


=={{header|Scala}}==
=={{header|Scala}}==
{{Out}}Best seen running in your browser either by [https://scalafiddle.io/sf/OuVZ3bT/0 ScalaFiddle (ES aka JavaScript, non JVM)] or [https://scastie.scala-lang.org/qW5qzmdKSZSyAbZEqDROoA Scastie (remote JVM)].
{{Out}}Best seen running in your browser either by [https://scalafiddle.io/sf/OuVZ3bT/0 ScalaFiddle (ES aka JavaScript, non JVM)] or [https://scastie.scala-lang.org/qW5qzmdKSZSyAbZEqDROoA Scastie (remote JVM)].
<lang Scala>object CreateMapObject extends App {
<syntaxhighlight lang="scala">object CreateMapObject extends App {
val map = Map('A' -> 65, 'B' -> 66, 'C' -> 67)
val map = Map('A' -> 65, 'B' -> 66, 'C' -> 67)


println(map)
println(map)
}</lang>
}</syntaxhighlight>


=={{header|Tcl}}==
=={{header|Tcl}}==
This solution uses a dict(ionary), so requires Tcl 8.5 or better. Variable traces are used to detect write or unset access to such a protected variable, restore it to the backup value at protection time, and throw an exception
This solution uses a dict(ionary), so requires Tcl 8.5 or better. Variable traces are used to detect write or unset access to such a protected variable, restore it to the backup value at protection time, and throw an exception


<lang Tcl>proc protect _var {
<syntaxhighlight lang="tcl">proc protect _var {
upvar 1 $_var var
upvar 1 $_var var
trace add variable var {write unset} [list protect0 $var]
trace add variable var {write unset} [list protect0 $var]
Line 1,360: Line 1,383:
puts "trying: $cmd"
puts "trying: $cmd"
if [catch {uplevel 1 $cmd} msg] {puts $msg}
if [catch {uplevel 1 $cmd} msg] {puts $msg}
}</lang>
}</syntaxhighlight>
Testing:
Testing:
dict set dic 1 one
dict set dic 1 one
Line 1,383: Line 1,406:


=={{header|Wren}}==
=={{header|Wren}}==
<lang ecmascript>class FixedSizeMap {
<syntaxhighlight lang="wren">class FixedSizeMap {
construct new(map) {
construct new(map) {
// copy the map so it cannot be mutated from the original reference
// copy the map so it cannot be mutated from the original reference
Line 1,435: Line 1,458:
System.print(fsm.values.toList)
System.print(fsm.values.toList)
for (me in fsm) System.print([me.key, me.value])
for (me in fsm) System.print([me.key, me.value])
</syntaxhighlight>
</lang>


{{out}}
{{out}}
Line 1,453: Line 1,476:
=={{header|zkl}}==
=={{header|zkl}}==
zkl has two dictionary objects: SD, a small dictionary that is created immutable and the "regular" dictionary has has a makeReadOnly method. They both behave the same when locked down.
zkl has two dictionary objects: SD, a small dictionary that is created immutable and the "regular" dictionary has has a makeReadOnly method. They both behave the same when locked down.
<lang zkl>d:=SD("one",1,"two",2);
<syntaxhighlight lang="zkl">d:=SD("one",1,"two",2);
d.keys; //-->L("one","two")
d.keys; //-->L("one","two")
d["one"]; //-->1
d["one"]; //-->1
d.add("three",3); // error thrown
d.add("three",3); // error thrown
d.pop("one") // error thrown</lang>
d.pop("one") // error thrown</syntaxhighlight>





Latest revision as of 14:15, 21 November 2023

Create an object/Native demonstration is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Create a Hash/Associative Array/Dictionary-like object that can be initialized with key/value pairs. The object should behave like a native Hash/Associative Array/Dictionary of the language, but with the following differences:

  • No new keys can be added;
  • Keys cannot be removed;
  • Attempting to delete a key should set that keys value back to that used during initialisation.

(The value assigned to keys may be changed by normal assignment however).

If the language supports Magic Methods, then show how these work.

BASIC

BASIC256

map mapa
mapa["A"] = 65
mapa["B"] = 66
mapa["C"] = 67

foreach valor in mapa
	print valor
	print mapa[valor]
next valor
Output:
A
65
B
66
C
67

FreeBASIC

FB doesn't have Dict natively, but we can implement them via Type

Type dict
    m1 As String*1
    m2 As Integer
End Type

Dim mapOf(1 To 3) As dict => {("A", 65), ("B", 66), ("C", 67)}

For i As Integer = 1 To Ubound(mapOf)
    Print mapOf(i).m1
    Print mapOf(i).m2
Next i
Output:
A
 65
B
 66
C
 67

C++

#include <iostream>
#include <map>
#include <utility>

using namespace std;

template<typename T>
class FixedMap : private T
{
    // Two standard maps are used to implement FixedMap.  One as a private
    // base class which will allow the values (but not the keys) to be modified.
    // Members of a private base class are not exposed to the derived class which will
    // prevent keys from being added or deleted. Another map will hold copies of
    // the initial values.
    T m_defaultValues;
    
public:
    FixedMap(T map)
    : T(map), m_defaultValues(move(map)){}
    
    // Expose members of the base class that do not modify the map.
    using T::cbegin;
    using T::cend;
    using T::empty;
    using T::find;
    using T::size;

    // Also expose members that can modify values but not add or remove keys.    
    using T::at;
    using T::begin;
    using T::end;
    
    // The [] operator will normally add a new key if the key is not already in the
    // map.  Instead, throw an error if the key is missing.
    auto& operator[](typename T::key_type&& key)
    {
        // Make it behave like at()
        return this->at(forward<typename T::key_type>(key));
    }
    
    // Instead of removing a key, change the sematics of erase() to restore
    // the original value of the key.
    void erase(typename T::key_type&& key)
    {
        T::operator[](key) = m_defaultValues.at(key);
    }

    // Also change the sematics of clear() to restore all keys
    void clear()
    {
        // Reset the base class using the defaults
        T::operator=(m_defaultValues);
    }
    
};

// Print the contents of a map
auto PrintMap = [](const auto &map)
{
    for(auto &[key, value] : map)
    {
        cout << "{" << key << " : " << value << "} ";
    }
    cout << "\n\n";
};

int main(void) 
{
    // Create a fixed map based on the standard map
    cout << "Map intialized with values\n";
    FixedMap<map<string, int>> fixedMap ({
        {"a", 1},
        {"b", 2}});
    PrintMap(fixedMap);
    
    cout << "Change the values of the keys\n";
    fixedMap["a"] = 55;
    fixedMap["b"] = 56;
    PrintMap(fixedMap);
    
    cout << "Reset the 'a' key\n";
    fixedMap.erase("a");
    PrintMap(fixedMap);
    
    cout << "Change the values the again\n";
    fixedMap["a"] = 88;
    fixedMap["b"] = 99;
    PrintMap(fixedMap);
    
    cout << "Reset all keys\n";
    fixedMap.clear();
    PrintMap(fixedMap);
  
    try
    {
        // Adding or retrieving a missing key is a run time error
        cout << "Try to add a new key\n";
        fixedMap["newKey"] = 99;
    }
    catch (exception &ex)
    {
        cout << "error: " << ex.what();
    }
}
Output:
Map intialized with values
{a : 1} {b : 2} 

Change the values of the keys
{a : 55} {b : 56} 

Reset the 'a' key
{a : 1} {b : 56} 

Change the values the again
{a : 88} {b : 99} 

Reset all keys
{a : 1} {b : 2} 

Try to add a new key
error: map::at

D

Translation of: Python
struct DefaultAA(TK, TV) {
    TV[TK] standard, current;

    this(TV[TK] default_) pure /*nothrow*/ @safe {
        this.standard = default_;
        this.current = default_.dup;
    }

    alias current this;

    void remove(in TK key) pure nothrow {
        current[key] = standard[key];
    }

    void clear() pure /*nothrow*/ @safe {
        current = standard.dup;
    }
}

void main() {
    import std.stdio;
    auto d = ["a": 1, "b": 2].DefaultAA!(string, int);

    d.writeln;                // ["a":1, "b":2]
    d["a"] = 55; d["b"] = 66;
    d.writeln;                // ["a":55, "b":66]
    d.clear;
    d.writeln;                // ["a":1, "b":2]
    d["a"] = 55; d["b"] = 66;
    d["a"].writeln;           // 55
    d.remove("a");
    d.writeln;                // ["a":1, "b":66]
}
Output:
["a":1, "b":2]
["a":55, "b":66]
["a":1, "b":2]
55
["a":1, "b":66]

Go

Go's built-in map type is mutable and so, to complete this task, we need to create a read-only wrapper for it which doesn't permit further items to be added or existing items to be deleted though does allow them to be reset to their default value.

First create a sub-directory, romap, of the project directory and place the following package in it:

package romap

type Romap struct{ imap map[byte]int }

//  Create new read-only wrapper for the given map.
func New(m map[byte]int) *Romap {
    if m == nil {
        return nil
    }
    return &Romap{m}
}

// Retrieve value for a given key, if it exists.
func (rom *Romap) Get(key byte) (int, bool) {
    i, ok := rom.imap[key]
    return i, ok
}

// Reset value for a given key, if it exists.
func (rom *Romap) Reset(key byte) {
    _, ok := rom.imap[key]
    if ok {
        rom.imap[key] = 0 // default value of int
    }
}

This package can now be imported and used within the main package as follows:

package main

import (
    "./romap"
    "fmt"
)

func main() {
    // create a normal map
    m := map[byte]int{'A': 65, 'B': 66, 'C': 67}

    // place it in a read-only wrapper so no new item can be added or item deleted.
    rom := romap.New(m)

    // retrieve value represented by 'C' say
    i, _ := rom.Get('C')
    fmt.Println("'C' maps to", i)

    // reset this to default value (doesn't actually delete the key)
    rom.Reset('C')
    i, _ = rom.Get('C')
    fmt.Println("'C' now maps to", i)
}
Output:
'C' maps to 67
'C' now maps to 0

J

Given a list of keys and an associated list of values, the idiomatic way of expressing this concept in J would be:

lookup=: values {~ keys&i.

For example:

   lookup=: 10 20 30 40 50 {~ (;:'this is a test')&i.
   lookup ;:'a test'
30 40

Notes:

1) While the result can not be modified or deleted, the name used to refer to it can be made to refer to something else, and once all references are lost it will be garbage collected.

2) In the above example, we have 5 values and 4 keys. The extra value is used when no key is found. If no extra value was provided, the "key not found" case would be an error case.

3) In J, objects are always referenced, but all data is passed by value. This means that objects can never be passed to a function -- only a reference to an object (its name) can be passed. This means that objects exist only in the way things are named, in J. So for the most part, we do not call things "objects" in J, and this task has nothing to do with what are called "objects" in J. However, this does demonstrate how things are created in J -- you write their definition, and can use them and/or assign to names or inspect them or whatever else.

Java

Java supports unmodifiable maps, sets, lists, and other more specialized unmodifiable collections. In this example, we have a unmodifiable map. We first create an ordinary map, modify as needed, then call the Collections.unmodifiableMap. We can subsequently read the map, but modification is not permitted. The returned map will subsequently throw a UnsupportedOperationException exception if a mutation operator is called. Several are demonstrated below.

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

//  Title:  Create an object/Native demonstration

public class ImmutableMap {

    public static void main(String[] args) {
        Map<String,Integer> hashMap = getImmutableMap();
        try {
            hashMap.put("Test", 23);
        }
        catch (UnsupportedOperationException e) {
            System.out.println("ERROR:  Unable to put new value.");
        }
        try {
            hashMap.clear();
        }
        catch (UnsupportedOperationException e) {
            System.out.println("ERROR:  Unable to clear map.");
        }
        try {
            hashMap.putIfAbsent("Test", 23);
        }
        catch (UnsupportedOperationException e) {
            System.out.println("ERROR:  Unable to put if absent.");
        }
        
        for ( String key : hashMap.keySet() ) {
            System.out.printf("key = %s, value = %s%n", key, hashMap.get(key));
        }
    }
    
    private static Map<String,Integer> getImmutableMap() {
        Map<String,Integer> hashMap = new HashMap<>();
        hashMap.put("Key 1", 34);
        hashMap.put("Key 2", 105);
        hashMap.put("Key 3", 144);

        return Collections.unmodifiableMap(hashMap);
    }
    
}

{out}}

ERROR:  Unable to put new value.
ERROR:  Unable to clear map.
ERROR:  Unable to put if absent.
key = Key 1, value = 34
key = Key 2, value = 105
key = Key 3, value = 144

JavaScript

This is a first demonstration of the task, but only implemented the functionality, not any native behavior, eg indexing. JavaScript experts may want to replace this one.

Works with: JavaScript version 1.7
var keyError = new Error("Invalid Key Error (FixedKeyDict)") ;

function FixedKeyDict(obj)
{    
    var myDefault = new Object() ;
    var myData    = new Object() ;
    for(k in obj) {
        myDefault[k] = obj[k] ;
        myData[k]    = obj[k] ;
    }

    var gotKey = function(k) {
        for(kk in myDefault) {
            if(kk == k) return true ;
        }
        return false ;        
    } ;

    this.hasKey = gotKey ;

    var checkKey = function(k) {
        if(!gotKey(k))
            throw keyError ;
    } ;
   
    this.getItem = function(k) {
        checkKey(k) ;
        return myData[k];
    } ;
    
    this.setItem = function(k, v) {
        checkKey(k) ;
        myData[k] = v ;
    } ;
    
    this.resetItem = function(k) {
        checkKey(k) ;
        myData[k] = myDefault[k] ;      
    } ;
    
    this.delItem = this.resetItem ;
    
    this.clear   = function() {
        for(k in myDefault)
            myData[k] = myDefault[k] ;
    } ;
    
    this.iterator = function() {
        for(k in myDefault)
            yield (k);            
    } ;
    
    this.clone    = function() {
        return new FixedKeyDict(myDefault) ;
    }
    
    this.toStr = function() {
        var s = "" ;
        for(key in myData)
            s = s + key + " => " + myData[key] + ", " ;
        return "FixedKeyDict{" + s + "}" ;
    } ; 
}

Test run:

const BR = "<BR>\n"

var pl = function(s) {
    document.write(s + BR) ;
} ;

pl("<pre>") ;

var o = { foo:101, bar:102 } ;

var h = new FixedKeyDict(o) ;
pl("Fixed Key Dict Created") ;
pl("toString   : " + h.toStr()) ;
pl("get an item: " + h.getItem("foo")) ;
pl("check a key: " + h.hasKey("boo")) ;
pl("ditto      : " + h.hasKey("bar")) ;
h.setItem("bar", 999) ;
pl("set an item: " + h.toStr()) ;
pl("Test iterator (or whatever)") ;
for(k in h.iterator())
    pl("  " + k + " => " + h.getItem(k)) ;
var g = h.clone() ;
pl("Clone a dict") ;
pl("  clone    : " + g.toStr()) ;
pl("  original : " + h.toStr()) ;
h.clear() ;
pl("clear or reset the dict") ;
pl("           : " + h.toStr()) ;
try {
    h.setItem("NoNewKey", 666 ) ;
} catch(e) {
    pl("error test : " + e.message) ;
}

output :

Fixed Key Dict Created
toString   : FixedKeyDict{foo => 101, bar => 102, }
get an item: 101
check a key: false
ditto      : true
set an item: FixedKeyDict{foo => 101, bar => 999, }
Test iterator (or whatever)
  foo => 101
  bar => 999
Clone a dict
  clone    : FixedKeyDict{foo => 101, bar => 102, }
  original : FixedKeyDict{foo => 101, bar => 999, }
clear or reset the dict
           : FixedKeyDict{foo => 101, bar => 102, }
error test : Invalid Key Error (FixedKeyDict)

jq

jq objects are JSON objects and can be created using JSON syntax, e.g.

{"language": "jq"}

Objects can also be created programmatically, e.g.

{"one": 1} + {"two": 2}

jq objects, however, are really just values: they are immutable, and cannot be "deleted" any more than the number 1 can be deleted.

Julia

using BackedUpImmutable

function testBackedUpImmutableDict()
    fibr = BackedUpImmutableDict{String,Int64}(["a" => 0, "b" => 1, "c" => 1, "d" => 2,
        "e" => 3, "f" => 5, "g" => 8, "h" => 13, "i" => 21, "j" => 34, "extra" => -1])

    x = fibr["extra"]
    @test x == -1
    fibr["extra"] = 0
    y = fibr["extra"]
    @test y == 0
    restore!(fibr, "extra")
    z = fibr["extra"]
    @test z == -1
    
    @test_throws String begin fibr["k"] = 55 end
 
    fibr["a"] = 9
    fibr["b"] = 7
    
    # test restore all to default
    restoreall!(fibr)
    
    @test fibr["a"] == 0
end

All tests pass.

Kotlin

// version 1.1.2

fun main(args: Array<String>) {
    // This line creates a read-only map which cannot be changed in any way nor cleared
    val map = mapOf('A' to 65, 'B' to 66, 'C' to 67)
    println(map)
}
Output:
{A=65, B=66, C=67}

M2000 Interpreter

Translation of: C sharp
Module CheckIt {
      Class LockedHash {
      Private:
            inventory Vars  ' no same keys
            unlock
            module nosuchvariable {
                  Error "No such value:"+letter$
            }
            module NoNewItem {
                  Error "No new item, use unlock method before"
            }
            module NoRemoveItem {
                  Error "Can't remove item, use unlock method before"
            }
      Public:
            module Unlock {
                  .unlock<=True
            }
            module Writeln {
                  m=each(.Vars)
                  while m {
                        Print Quote$(Eval$(m, m^));",";Eval(m),
                  }
                  Print
            }
            Value (st$){
                  st$=Ucase$(st$)
                  if exist(.Vars, st$)  then =Eval(.Vars) : Exit
                  .nosuchvariable st$
            }
            Set (st$){
                  st$=Ucase$(st$)
                  Read val
                  if exist(.Vars, st$)  then Return .Vars, st$:=val : Exit
                  If .unlock then { Append .Vars, st$:=val} Else .NoNewItem
            }
            module Remove (st$) {
                  if not .unlock then .NoRemoveItem
                  st$=Ucase$(st$)
                  Try {
                        delete .Vars, st$
                  }
            }
            module Clear {
                  Clear .Vars
            }
      Class:  ' this part exist only at construction
            module LockedHash {
                  While match("SN") {
                        read st$, val
                        st$=ucase$(st$)
                        \\ if we append key which exist we get error
                        Append .Vars, st$:=val
                  }
            }
      }
      d=LockedHash("a", 1, "b", 2)
      d.writeln
      d("a")=55 : d("b")=66
      d.writeln
      d.clear
      d.writeln 
      d.unlock
      d("a")=55 : d("b")=66
      Print d("a")=55, d("a")/d("b")<1
      d.remove "a"
      d.writeln
}
Checkit

Mathematica / Wolfram Language

a[1] = "Do not modify after creation";
a[2] = "Native demonstration";
Protect[a];

Example usage:

a[3] = 2
->Set::write: Tag a in a[1] is Protected. >>

Nim

We leverage native stdlib table as our own object by implementing limited actual native table functionalities.

import tables, options

type
  MyTable = object
    table: TableRef[string, int]

# return empty if the key is not available
proc `[]`(m: MyTable, key: string): Option[int] =
  if key in m.table: result = some m.table[key]
  else: result = none int

# update an item, doing nothing if the key is available during first initialization
proc `[]=`(m: var MyTable, key: string, val: int) =
  if key notin m.table: return
  m.table[key] = val

proc reset(m: var MyTable) =
  for _, v in m.table.mpairs: v = 0

# sugar for defining MyTable object
proc toTable(vals: openarray[(string, int)]): MyTable =
  result.table = newTable vals

proc main =
  # MyTable construction
  var myobj = {"key1": 1, "key2": 2, "key3": 3}.toTable
  # test getting existing key
  let val1 = myobj["key1"]
  if val1.isSome: echo "val1: ", val1.get

  # test adding new key
  myobj["key4"] = 4
  let val4 = myobj["key4"]
  if val4.isSome: echo val4.get
  else: echo "val4 is empty"

  # test reset and test whether its value is zero-ed
  reset myobj
  doAssert myobj["key3"].get == 0

main()
Output:
val1: 1
val4 is empty

Perl

use strict;

package LockedHash;
use parent 'Tie::Hash';
use Carp;

sub TIEHASH {
	my $cls = shift;
	my %h = @_;
	bless \%h, ref $cls || $cls;
}

sub STORE {
	my ($self, $k, $v) = @_;
	croak "Can't add key $k" unless exists $self->{$k};
	$self->{$k} = $v;
}

sub FETCH {
	my ($self, $k) = @_;
	croak "No key $k" unless exists $self->{$k};
	$self->{$k};
}

sub DELETE {
	my ($self, $k) = @_;
	croak "No key $k" unless exists $self->{$k};
	$self->{$k} = 0;
}

sub CLEAR { } # ignored
sub EXISTS { exists shift->{+shift} }

sub FIRSTKEY {
	my $self = shift;
	keys %$self;
	each %$self;
}

sub NEXTKEY {
	my $self = shift;
	each %$self;
}

sub lock_hash :prototype(\%) {
	my $ref = shift;
	tie(%$ref, __PACKAGE__, %$ref);
}

1;

my %h = (a => 3, b => 4, c => 5);

# lock down %h
LockedHash::lock_hash(%h);

# show hash content and iteration
for (sort keys %h) { print "$_ => $h{$_}\n"; }

# try delete b
delete $h{b};
print "\nafter deleting b: b => $h{b}\n";

# change value of a
$h{a} = 100;
print "\na => $h{a}\n";

# add a new key x: will die
eval { $h{x} = 1 };
if ($@) { print "Operation error: $@" }

output:

a => 3
b => 4
c => 5

after deleting b: b => 0

a => 100
operation error: Can't add key x at test.pl line 14
        LockedHash::STORE('LockedHash=HASH(0x8cebe14)', 'x', 1) called at test.pl line 66
        eval {...} called at test.pl line 66

Phix

There is no native "read-only" setting on phix dictionaries, so the following wraps a pair of them to provide the requested functionality.

with javascript_semantics
enum STD, CUR
sequence fkds = {}      -- fixed key dictionaries ;-)
integer freelist = 0
 
procedure fkd_destroy(integer id)
    integer {std,cur} = fkds[id]
    destroy_dict(std)
    destroy_dict(cur)
    fkds[id] = freelist
    freelist = id
end procedure
 
function fkd_new(sequence key_pairs)
    integer std = new_dict(key_pairs),
            cur = new_dict(std),
            id = freelist
    if id=0 then
        fkds = append(fkds,{std,cur})
        id = length(fkds)
    else
        freelist = fkds[id]
        fkds[id] = {std,cur}
    end if
    return id
end function
 
procedure fkd_clear(integer id)
    integer {std,cur} = fkds[id]
    destroy_dict(cur)
    fkds[id][CUR] = new_dict(std)
end procedure
 
function fkd_get(integer id, object key)
    return getd(key,fkds[id][CUR])
end function
 
procedure fkd_set(integer id, object key, data)
    integer node = getd_index(key,fkds[id][CUR])
    if node=NULL then throw("invalid/new key") end if
    setd(key,data,fkds[id][CUR])
end procedure
 
procedure fkd_remove(integer id, object key)
    integer {std,cur} = fkds[id],
            node = getd_index(key,std)
    if node=NULL then throw("invalid key") end if
    setd(key,getd_by_index(node,std),cur)
end procedure
 
function fkd_sprint(integer id)
    integer cur = fkds[id][CUR]
    sequence res = getd_all_keys(cur)
    for i=1 to length(res) do
        object ri = res[i]
        res[i] = {ri,getd(ri,cur)}
    end for
    return res
end function
 
procedure main()
    integer id = fkd_new({{"a",1},{"b",2}})
    ?fkd_sprint(id)                         -- {{"a",1},{"b",2}}
    fkd_set(id,"a",55)
    fkd_set(id,"b",66)
    ?fkd_sprint(id)                         -- {{"a",55},{"b",66}}
    fkd_clear(id)
    ?fkd_sprint(id)                         -- {{"a",1},{"b",2}}
    fkd_set(id,"a",55)
    fkd_set(id,"b",66)
    ?fkd_get(id,"a")                        -- 55
    fkd_remove(id,"a")
    try
        fkd_set(id,"NoNewKey",77)
    catch e
        ?e[E_USER]                          -- "invalid/new key"
    end try
    ?fkd_sprint(id)                         -- {{"a",1},{"b",66}}
    fkd_destroy(id)
end procedure
main()

Python

from collections import UserDict
import copy

class Dict(UserDict):
    '''
    >>> d = Dict(a=1, b=2)
    >>> d
    Dict({'a': 1, 'b': 2})
    >>> d['a'] = 55; d['b'] = 66
    >>> d
    Dict({'a': 55, 'b': 66})
    >>> d.clear()
    >>> d
    Dict({'a': 1, 'b': 2})
    >>> d['a'] = 55; d['b'] = 66
    >>> d['a']
    55
    >>> del d['a']
    >>> d
    Dict({'a': 1, 'b': 66})
    '''
    def __init__(self, dict=None, **kwargs):
        self.__init = True
        super().__init__(dict, **kwargs)
        self.default = copy.deepcopy(self.data)
        self.__init = False
    
    def __delitem__(self, key):
        if key in self.default:
            self.data[key] = self.default[key]
        else:
            raise NotImplementedError

    def __setitem__(self, key, item):
        if self.__init:
            super().__setitem__(key, item)
        elif key in self.data:
            self.data[key] = item
        else:
            raise KeyError

    def __repr__(self):
        return "%s(%s)" % (type(self).__name__, super().__repr__())
    
    def fromkeys(cls, iterable, value=None):
        if self.__init:
            super().fromkeys(cls, iterable, value)
        else:
            for key in iterable:
                if key in self.data:
                    self.data[key] = value
                else:
                    raise KeyError

    def clear(self):
        self.data.update(copy.deepcopy(self.default))

    def pop(self, key, default=None):
        raise NotImplementedError

    def popitem(self):
        raise NotImplementedError

    def update(self, E, **F):
        if self.__init:
            super().update(E, **F)
        else:
            haskeys = False
            try:
                keys = E.keys()
                haskeys = Ture
            except AttributeError:
                pass
            if haskeys:
                for key in keys:
                    self[key] = E[key]
            else:
                for key, val in E:
                    self[key] = val
            for key in F:
                self[key] = F[key]

    def setdefault(self, key, default=None):
        if key not in self.data:
            raise KeyError
        else:
            return super().setdefault(key, default)

Racket

Translation of: D

This task is implemented as a new fenced-hash time with an interface similar to the native hash. Also it can be used a native dict.

Implementation of functions that handle fenced-hash:

;(struct fenced-hash (actual original) ...)

(define (fenced-hash-ref dict 
                         key 
                         [default (lambda () (error "key not found" key))]) 
  (hash-ref (fenced-hash-actual dict) key default)) 
(define (fenced-hash-set! dict key val) 
  (unless (hash-has-key? (fenced-hash-actual dict)  key)
    (error "unable to add key" key))
  (hash-set! (fenced-hash-actual dict) key val)) 
(define (fenced-hash-remove! dict key) ;reset the value! 
  (unless (hash-has-key? (fenced-hash-actual dict) key)
    (error "key not found" key))
  (hash-set! (fenced-hash-actual dict) 
             key
            (hash-ref (fenced-hash-original dict) key))) 
(define (fenced-hash-clear! dict) ;reset all values! 
  (hash-for-each (fenced-hash-original dict) 
                 (lambda (key val) (hash-set! (fenced-hash-actual dict) key val))))

(define (fenced-hash-has-key? dict key) 
  (hash-has-key? (fenced-hash-actual dict) key))
(define (fenced-hash-count dict)
  (hash-count (fenced-hash-actual dict)))

(define (fenced-hash-iterate-first dict) 
  (hash-iterate-first (fenced-hash-actual dict)))
(define (fenced-hash-iterate-next dict pos) 
  (hash-iterate-next (fenced-hash-actual dict) pos))
(define (fenced-hash-iterate-key dict pos) 
  (hash-iterate-key (fenced-hash-actual dict) pos))
(define (fenced-hash-iterate-value dict pos) 
  (hash-iterate-value (fenced-hash-actual dict) pos))

(define (*fenced-hash-print dict port mode) 
        ;private custom-write ;mode is ignored
     (write-string "#fenced-hash" port)
     (write (hash->list (fenced-hash-actual dict)) port))

Definition of the actual structure and a “public” creator:

(struct fenced-hash (actual original)
  #:extra-constructor-name *fenced-hash ;private constructor
  #:omit-define-syntaxes ;not sure this is a good idea
  #:methods gen:custom-write 
  [(define write-proc *fenced-hash-print)]

  #:methods gen:dict 
  [(define dict-ref fenced-hash-ref)
   (define dict-set! fenced-hash-set!) 
   (define dict-remove! fenced-hash-remove!)
   (define dict-has-key? fenced-hash-has-key?) ;unused in 5.6.3
   (define dict-count fenced-hash-count)
   (define dict-iterate-first fenced-hash-iterate-first)
   (define dict-iterate-next fenced-hash-iterate-next)
   (define dict-iterate-key fenced-hash-iterate-key)
   (define dict-iterate-value fenced-hash-iterate-value)])


(define (fenced-hash . args) ; public constructor
  (define original (apply hash args))
  (*fenced-hash (hash-copy original) original))

Example: Use the fenced-hash functions:

(define d (fenced-hash "a" 1 "b" 2))

(displayln d)
(fenced-hash-set! d "a" 55)
(fenced-hash-set! d "b" 66)
(displayln d)
(fenced-hash-clear! d)
(displayln d)
(fenced-hash-set! d "a" 55)
(fenced-hash-set! d "b" 66)
(displayln d)
(fenced-hash-remove! d "a")
(displayln d)
Output:
#fenced-hash(("b" . 2) ("a" . 1))
#fenced-hash(("b" . 66) ("a" . 55))
#fenced-hash(("b" . 2) ("a" . 1))
#fenced-hash(("b" . 66) ("a" . 55))
#fenced-hash(("b" . 66) ("a" . 1))

Example (continued): Use the same object as a dict. The dict-clear! method is not defined, so we must call fenced-hash-clear! instead.

(fenced-hash-clear! d)
(displayln d)
(dict-set! d "a" 55)
(dict-set! d "b" 66)
(displayln d)
(fenced-hash-clear! d) ;dict-clear is not defined
(displayln d)
(dict-set! d "a" 55)
(dict-set! d "b" 66)
(displayln d)
(dict-remove! d "a")
(displayln d)
Output:
#fenced-hash(("b" . 2) ("a" . 1))
#fenced-hash(("b" . 66) ("a" . 55))
#fenced-hash(("b" . 2) ("a" . 1))
#fenced-hash(("b" . 66) ("a" . 55))
#fenced-hash(("b" . 66) ("a" . 1))

Raku

(formerly Perl 6)

Works with: rakudo version 2016.08

Here we use delegation to handle all the normal hash methods that we don't need to override to define our new class.

class FixedHash {
        has $.hash handles *;
        method new(*@args) { self.bless: hash => Hash.new: @args }
        method AT-KEY(FixedHash:D: $key is copy) is rw {
                $!hash.EXISTS-KEY($key) ?? $!hash.AT-KEY($key) !! Failure.new(q{can't store value for unknown key});
        }
        method DELETE-KEY($key) { $!hash.{$key} = Nil }
}

# Testing
my $fh = FixedHash.new: "a" => 1, "b" => 2;
say $fh<a b>;   # 1 2
$fh<b>:delete;
say $fh<a b>;   # 1 Nil
$fh<b> = 42;
say $fh<a b>;   # 1 42
say $fh<c>;     # Nil
$fh<c> = 43;    # error
Output:
(1 2)
(1 (Any))
(1 42)
can't store value for unknown key
  in block <unit> at native-demonstration.p6:17

Actually thrown at:
  in block <unit> at native-demonstration.p6:17

By defining FALLBACK any class can handle undefined method calls. Since any class inherits plenty of methods from Any our magic object will be more of a novice conjurer then a master wizard proper.

class Magic {
        has %.hash;
        multi method FALLBACK($name, |c) is rw { # this will eat any extra parameters
                %.hash{$name}
        }

        multi method FALLBACK($name) is rw {
                %.hash{$name}
        }
}

my $magic = Magic.new;
$magic.foo = 10;
say $magic.foo;
$magic.defined = False; # error
Output:
10
Cannot modify an immutable Bool
  in block <unit> at native-demonstration.p6:15

Ring

# Project : Create an object/Native demonstration

map = []
map["A"] = 65
map["B"] = 66
map["C"] = 67
see map + nl

Output:

A
65
B
66
C
67

Ruby

Works with: Ruby version 1.9
# A FencedHash acts like a Hash, but with a fence around its keys.
# One may change its values, but not its keys.  Any attempt to insert
# a new key raises KeyError.  One may delete a key, but this only
# restores its original value.
#
# FencedHash reimplements these Hash methods: #[] #[]= #clear #delete
# #delete_if #default #default= #each_key #each_pair #each_value
# #fetch #has_key? #keep_if #keys #length #values #values_at
class FencedHash

  # call-seq:
  #   FencedHash.new(hash, obj=nil)  -> fh
  #
  # Creates a FencedHash that takes its keys and original values from
  # a source _hash_.  The source _hash_ can be any object that
  # responds to each_pair.  Sets the default value for missing keys to
  # _obj_, so FencedHash#[] returns _obj_ when a key is not in fence.
  def initialize(hash, obj=nil)
    @default = obj
    @hash = {}
    hash.each_pair do |key, value|
      # @hash[key][0] = current value
      # @hash[key][1] = original value
      @hash[key] = [value, value]
    end
  end

  def initialize_clone(orig)
    # Object#clone calls here in Ruby 2.0.  If _orig_ was frozen, then
    # each array of _values_ is frozen, so make frozen clones.
    super
    copy = {}
    @hash.each_pair {|key, values| copy[key] = values.clone }
    @hash = copy
  end

  def initialize_dup(orig)
    # Object#dup calls here in Ruby 2.0.  If _orig_ was frozen, then
    # make duplicates that are not frozen.
    super
    copy = {}
    @hash.each_pair {|key, values| copy[key] = values.dup }
    @hash = copy
  end

  # Retrieves current value for _key_, like Hash#[].  If _key_ is not
  # in fence, returns default object.
  def [](key)
    values = @hash[key]
    if values
      values[0]
    else
      @default
    end
  end

  # call-seq:
  #   fh[key] = value       -> value
  #   fh.store(key, value)  -> value
  #
  # Sets _value_ for a _key_.  Returns _value.  If _key_ is not in
  # fence, raises KeyError.
  def []=(key, value)
    values = @hash[key]
    if values
      values[0] = value
    else
      raise KeyError, "fence prevents adding new key: #{key.inspect}"
    end
  end
  alias store []=

  # Resets all keys to their original values.  Returns self.
  def clear
    @hash.each_value {|values| values[0] = values[1]}
    self
  end

  # Resets _key_ to its original value.  Returns old value before
  # reset.  If _key_ is not in fence, returns +nil+.
  def delete(key)
    values = @hash[key]
    if values
      old = values[0]
      values[0] = values[1]
      old  # return old
    end    # else return nil
  end

  # call-seq:
  #   fh.delete_if {|key, value| block }  -> fh
  #   fh.delete_if                        -> enumerator
  #
  # Yields each _key_ with current _value_ to _block_.  Resets _key_
  # to its original value when block evaluates to true.
  def delete_if
    if block_given?
      @hash.each_pair do |key, values|
        yield(key, values[0]) and values[0] = values[1]
      end
      self
    else
      enum_for(:delete_if) { @hash.size }
    end
  end

  # The default value for keys not in fence.
  attr_accessor :default

  # call-seq:
  #   fh.each_key {|key| block}  -> fh
  #   fh.each_key                -> enumerator
  #
  # Yields each key in fence to the block.
  def each_key(&block)
    if block
      @hash.each_key(&block)
      self
    else
      enum_for(:each_key) { @hash.size }
    end
  end

  # call-seq:
  #   fh.each_pair {|key, value| block}  -> fh
  #   fh.each_pair                       -> enumerator
  #
  # Yields each key-value pair to the block, like Hash#each_pair.
  # This yields each [key, value] as an array of 2 elements.
  def each_pair
    if block_given?
      @hash.each_pair {|key, values| yield [key, values[0]] }
      self
    else
      enum_for(:each_pair) { @hash.size }
    end
  end

  # call-seq
  #   fh.each_value {|value| block} -> fh
  #   fh.each_value                 -> enumerator
  #
  # Yields current value of each key-value pair to the block.
  def each_value
    if block_given?
      @hash.each_value {|values| yield values[0] }
    else
      enum_for(:each_value) { @hash.size }
    end
  end

  # call-seq:
  #   fenhsh.fetch(key [,default])
  #   fenhsh.fetch(key) {|key| block }
  #
  # Fetches value for _key_.  Takes same arguments as Hash#fetch.
  def fetch(*argv)
    argc = argv.length
    unless argc.between?(1, 2)
      raise(ArgumentError,
            "wrong number of arguments (#{argc} for 1..2)")
    end
    if argc == 2 and block_given?
      warn("#{caller[0]}: warning: " +
           "block supersedes default value argument")
    end

    key, default = argv
    values = @hash[key]
    if values
      values[0]
    elsif block_given?
      yield key
    elsif argc == 2
      default
    else
      raise KeyError, "key not found: #{key.inspect}"
    end
  end

  # Freezes this FencedHash.
  def freeze
    @hash.each_value {|values| values.freeze }
    super
  end

  # Returns true if _key_ is in fence.
  def has_key?(key)
    @hash.has_key?(key)
  end
  alias include? has_key?
  alias member? has_key?

  # call-seq:
  #   fh.keep_if {|key, value| block }  -> fh
  #   fh.keep_if                        -> enumerator
  #
  # Yields each _key_ with current _value_ to _block_.  Resets _key_
  # to its original value when block evaluates to false.
  def keep_if
    if block_given?
      @hash.each_pair do |key, values|
        yield(key, values[0]) or values[0] = values[1]
      end
      self
    else
      enum_for(:keep_if) { @hash.size }
    end
  end

  # Returns array of keys in fence.
  def keys
    @hash.keys
  end

  # Returns number of key-value pairs.
  def length
    @hash.length
  end
  alias size length

  # Converts self to a regular Hash.
  def to_h
    result = Hash.new(@default)
    @hash.each_pair {|key, values| result[key] = values[0]}
    result
  end

  # Converts self to a String.
  def to_s
    "#<#{self.class}: #{to_h}>"
  end
  alias inspect to_s

  # Returns array of current values.
  def values
    @hash.each_value.map {|values| values[0]}
  end

  # Returns array of current values for keys, like Hash#values_at.
  def values_at(*keys)
    keys.map {|key| self[key]}
  end
end

Scala

Output:

Best seen running in your browser either by ScalaFiddle (ES aka JavaScript, non JVM) or Scastie (remote JVM).

object CreateMapObject extends App {
  val map = Map('A' -> 65, 'B' -> 66, 'C' -> 67)

  println(map)
}

Tcl

This solution uses a dict(ionary), so requires Tcl 8.5 or better. Variable traces are used to detect write or unset access to such a protected variable, restore it to the backup value at protection time, and throw an exception

proc protect _var {
    upvar 1 $_var var
    trace add variable var {write unset} [list protect0 $var]
}
proc protect0 {backup name1 name2 op} {
    upvar 1 $name1 var
    trace remove variable var {write unset} [list protect 0 $backup]
    set var $backup
    trace add variable var {write unset} [list protect0 $backup]
    return -code error "$name1 is protected"
}
proc trying cmd { #-- convenience function for demo
    puts "trying: $cmd"
    if [catch {uplevel 1 $cmd} msg] {puts $msg}
}

Testing:

dict set dic 1 one 
dict set dic 2 two
puts dic:$dic
protect dic
trying "dict set dic 3 three"
puts dic:$dic
trying "dict unset dic 1"
trying "unset dic"
puts dic:$dic

displays on stdout:

dic:1 one 2 two
trying: dict set dic 3 three
can't set "dic": dic is protected
dic:1 one 2 two
trying: dict unset dic 1
can't set "dic": dic is protected
trying: unset dic
dic:1 one 2 two

Wren

class FixedSizeMap {
    construct new(map) {
        // copy the map so it cannot be mutated from the original reference
        _map = {}
        for (me in map.toList) _map[me.key] = me.value
    }

    containsKey(key) { _map[key] != null }

    count { _map.count }

    keys { _map.keys }

    values { _map.values }

    [key] { _map[key] }
    [key] =(value) { 
        // do nothing if key doesn't already exist
        if (_map[key] != null) _map[key] = value
    }

    reset(key) {
        var t = _map[key].type
        // leave unaltered if no suitable default value
        _map[key] = (t == Num)    ? 0 :
                    (t == String) ? "":
                    (t == Bool)   ? false :
                    (t == List)   ? [] :
                    (t == Map)    ? {} : _map[key] 
    }    

    iterate(iterator)       { _map.iterate(iterator) }
    iteratorValue(iterator) { _map.iteratorValue(iterator) }

    toString { _map.toString }
}

var map = { "a": 1, "b": 2 }
var fsm = FixedSizeMap.new(map)
System.print(fsm)
System.print(fsm.count)
fsm["a"] = 3
fsm["b"] = 4
System.print(fsm)
System.print(fsm.containsKey("c"))
fsm["c"] = 5 // attempt to add a new key/value pair
System.print(fsm) // ignored
fsm.reset("a")
System.print(fsm)
System.print(fsm.keys.toList)
System.print(fsm.values.toList)
for (me in fsm) System.print([me.key, me.value])
Output:
{b: 2, a: 1}
2
{b: 4, a: 3}
false
{b: 4, a: 3}
{b: 4, a: 0}
[b, a]
[4, 0]
[b, 4]
[a, 0]

zkl

zkl has two dictionary objects: SD, a small dictionary that is created immutable and the "regular" dictionary has has a makeReadOnly method. They both behave the same when locked down.

d:=SD("one",1,"two",2);
d.keys;   //-->L("one","two")
d["one"]; //-->1
d.add("three",3); // error thrown
d.pop("one")   // error thrown