Thread overview
How to get rid of const / immutable poisoning (and I didn't even want to use them...)
May 22, 2017
Enjoys Math
May 22, 2017
Enjoys Math
May 22, 2017
ag0aep6g
May 22, 2017
Enjoys Math
May 23, 2017
Kagamin
May 22, 2017
I had to employ const / immutable to some things to get passed some compiler errors.  Then the poisoning continues.

How do I get this code to run?

String's will hold a T[] which actually will not be modified by the String methods ("immutable strings of T").

I did not want to use any immutable / const anywhere at first, but by passing in [1,2,3]
May 22, 2017
On Monday, 22 May 2017 at 20:12:56 UTC, Enjoys Math wrote:
> I had to employ const / immutable to some things to get passed some compiler errors.  Then the poisoning continues.
>
> How do I get this code to run?
>
> String's will hold a T[] which actually will not be modified by the String methods ("immutable strings of T").
>
> I did not want to use any immutable / const anywhere at first, but by passing in [1,2,3]


Crap.  I hit tab then enter.

By passing in a constant array which is totally legit for testing reasons, the compiler shits.

Here's the code:
------------

module smallest_grammar;
import std.conv;
import std.algorithm;

struct Symbol(T) {
public:
	this(T sym, bool isVar) {
		this.sym = sym;
		this.is_var = isVar;
	}

	@property T symbol() { return sym; }
	@property bool isVar() { return is_var; }


private:
	T sym;
	bool is_var = false;
}


immutable struct String(T) {
public:
	this(immutable T[] s) {
		this.s = s;
	}
	
	alias s this;

	string toString() const { return to!string(s); }
	string flattened() const { string t = "";  foreach (c; s)	t ~= to!string(c);  return t; }

	size_t toHash() const @system pure nothrow
    {
		return hashOf(flattened());
    }

    bool opEquals(ref const String t) const @system pure nothrow
    {
		if (t.length != this.length)
			return false;

		for(size_t k=0; k < t.length; k++)
			if (t.s[k] != this.s[k]) return false;

		return true;
    }

	// A compressible is a substring that occurs (non-overlappingly) >=2 and has a length >= 3
	// or one that occurs >= 3 and has a length of 2
	String[] compressibles() const {
		auto count = compressibleCount();
		String[] substrings;
		foreach (s, n; count)
			substrings ~= s;
		return substrings;
	}

	size_t[String] compressibleCount() const {
		auto count = substringCount(2, size_t(s.length / 2));

		foreach (str, n; count)
			if (str.length == 2 && n < 3 || str.length >= 3 && n < 2)
				count.remove(str);

		return count;
	}

	String[] substrings(int minLen=0, int maxLen=-1) const {
		auto count = substringCount();
		String[] substrings;
		foreach (s, n; count)
			substrings ~= s;
		return substrings;
	}


	size_t[String] substringCount(int minLen=0, int maxLen=-1) const {
		if (maxLen == -1)
			maxLen = s.length;
		assert (maxLen <= s.length && minLen >=0 && minLen <= maxLen);

		size_t[String] count;

		if (minLen == 0)
			count[empty] = s.length + 1;
		
		for (size_t i=0; i < s.length - minLen; i++) {

			size_t max_len = min(s.length - i, maxLen);

			for (size_t l=1; l < max_len; l++) {
				auto substr = String(this.s[i..i+l]);

				if (!(substr in count))
					count[substr] = 0;
				else
					count[substr] ++;
			}
		}

		return count;
	}

public:
	immutable(T)[] empty = [];

private
	T[] s;
}


struct Grammar(T) {
public:
	this(string name) { this.name = name; }

private:
	string name;
	String!(T)[][T] rules;
}


unittest {
	import std.stdio;

	// String
	auto s = String!string(["1", "2", "34"]);
	writeln(s);
	writeln(s.flattened());

	// Compressibles
	s = String!string(["a", "b", "a", "b", "a", "b", "a"]);
	writeln(s.substrings());

	// Grammar
	auto g = Grammar!string();




	writeln("~ End Smallest Grammar unittests ~");
}
May 22, 2017
On Monday, 22 May 2017 at 20:12:56 UTC, Enjoys Math wrote:
> I had to employ const / immutable to some things to get passed some compiler errors.  Then the poisoning continues.
>
> How do I get this code to run?
>
> String's will hold a T[] which actually will not be modified by the String methods ("immutable strings of T").
>
> I did not want to use any immutable / const anywhere at first, but by passing in [1,2,3]


Solved it by reverting back to original code (no const / immutable) on struct and creating two constructors:

	this(T[] s) {
		this.s = s;
	}

	this(const(T)[] s) {
		this.s = cast(T[]) s;
	}
May 22, 2017
On 05/22/2017 10:13 PM, Enjoys Math wrote:
> immutable struct String(T) {

Marking a whole struct as `immutable` is usually a mistake. It applies to all methods, making them unusable on mutable and const instances.

> public:
>      this(immutable T[] s) {

This means you can create `String`s only with immutable arrays. Again, not what you want usually. You can use `inout` to make a constructor that accepts mutable, const, and immutable arrays when constructing mutable, const, and immutable instances, respectively:

----
this(inout T[] s) inout { ... }
----

If `inout` doesn't work for some reason, you will have to define distinct constructors:

----
this(T[] s) { ... }
this(const T[] s) const { ... }
this(immutable T[] s) immutable { ... }
----

>          this.s = s;
>      }
> 
>      alias s this;
> 
>      string toString() const { return to!string(s); }
>      string flattened() const { string t = "";  foreach (c; s)    t ~= to!string(c);  return t; }
> 
>      size_t toHash() const @system pure nothrow
>      {
>          return hashOf(flattened());
>      }
> 
>      bool opEquals(ref const String t) const @system pure nothrow
>      {
>          if (t.length != this.length)
>              return false;
> 
>          for(size_t k=0; k < t.length; k++)

Off topic: foreach (k; 0 .. t.length)

>              if (t.s[k] != this.s[k]) return false;

Seeing how `k` is used, could also make it:

----
foreach (k, e; t)
    if (e != this.s[k]) return false;
----

>          return true;
>      }
> 
>      // A compressible is a substring that occurs (non-overlappingly)  >=2 and has a length >= 3
>      // or one that occurs >= 3 and has a length of 2
>      String[] compressibles() const {
>          auto count = compressibleCount();
>          String[] substrings;
>          foreach (s, n; count)
>              substrings ~= s;
>          return substrings;
>      }
> 
>      size_t[String] compressibleCount() const {
>          auto count = substringCount(2, size_t(s.length / 2));

Off topic: substringCount's second parameter is an int, not a size_t. Converting from size_t to int like this works with -m32, but if large values are possible, you've got a bug. With -m64 it simply doesn't work. You can use `to!int` which throws on failure.

>          foreach (str, n; count)
>              if (str.length == 2 && n < 3 || str.length >= 3 && n < 2)
>                  count.remove(str);
> 
>          return count;
>      }
> 
>      String[] substrings(int minLen=0, int maxLen=-1) const {
>          auto count = substringCount();
>          String[] substrings;
>          foreach (s, n; count)
>              substrings ~= s;
>          return substrings;
>      }
> 
> 
>      size_t[String] substringCount(int minLen=0, int maxLen=-1) const {
>          if (maxLen == -1)
>              maxLen = s.length;

Same bug-prone conversion from size_t to int as above.

>          assert (maxLen <= s.length && minLen >=0 && minLen <= maxLen);
> 
>          size_t[String] count;
> 
>          if (minLen == 0)
>              count[empty] = s.length + 1;

`empty` is not a String, it's a T[]. Works when you change `empty` to `static empty = String([]);`.

>          for (size_t i=0; i < s.length - minLen; i++) {
> 
>              size_t max_len = min(s.length - i, maxLen);
> 
>              for (size_t l=1; l < max_len; l++) {
>                  auto substr = String(this.s[i..i+l]);

We're in a const method, so `this.s[i..i+l]` is const as well. If you want to return a result with mutable keys, you have to `.dup` that slice of s.

Alternatively, drop `const` from the method, or return a result with const keys. Though returning `const` has similar repercussions as returning immutable keys (next paragraphs).

Arguably, keys of associative arrays should be immutable. When the keys change and the associative isn't re-hashed, it's going to behave erratically.

Making the keys immutable would mean you have to `.idup` the slices. You'd also have `.dup` in `compressibles` and `substring` if they're supposed to return mutable `String`s.

>                  if (!(substr in count))
>                      count[substr] = 0;
>                  else
>                      count[substr] ++;
>              }
>          }
> 
>          return count;
>      }
> 
> public:
>      immutable(T)[] empty = [];
> 
> private
>      T[] s;
> }
May 23, 2017
https://dpaste.dzfl.pl/74d67cfca3e8