<div id="geary-body" dir="auto"><div>Greetings @cafe,</div><div><br></div><div>Transparent wrapping of string literals into custom types, driven automatically by type inference, is of course very useful. I'm getting huge mileage off GHC's <font face="monospace">OverloadedStrings</font> when working with e.g. the <font face="monospace">Text</font> type:</div><div><br></div><div><pre><span class="kr">class</span> <span class="kt">IsString</span> <span class="n">a</span> <span class="kr">where</span>
  <span class="n">fromString</span> <span class="ow">::</span> <span class="kt">String</span> <span class="ow">-></span> <span class="n">a</span></pre><pre><span class="n"><br></span></pre><pre><span class="n">instance IsString Text {- ... -}</span></pre><pre><span class="n"><br></span></pre><pre><span class="n">greeting :: Text</span></pre><pre><span class="n">greeting = "Hello"</span></pre></div><div><br></div><div>But I hope there's possibly room for improvement. I've got a bag of several use-cases; perhaps the simplest is the following.</div><div><br></div><div>Consider a type called <font face="monospace">NonEmptyText</font> which is a (wrapper around) Text, but with an added invariant that the contained text is never the empty string "".</div><div><br></div><div>Depending on codebase, such a type can be very useful too, allowing to offload the burden of keeping track of the invariant onto machine; the typechecker will show me all the spots to handle, current or future ones. IOW, this type enables writing a non-boolean-blind null check, <font face="monospace">toNonEmptyText :: ToText str => str -> Maybe NonEmptyText</font> — which makes not only the programmer, but also the compiler, aware of the null-check result.</div><div><br></div><div>I'm focusing the example on Text to somewhat maintain generality of the idea. <font face="monospace">NonEmpty Char</font> can be an alternative formulation of "non-empty String"; length-indexed vectors could possibly handle this too. But there're all sorts of invariants besides non-emptiness one may wish to express.</div><div><br></div><div>I'll omit other examples; but to summarize, I've got plenty of practical motivation to get "smart literals".</div><div><br></div><div>And to the point: <font face="monospace">IsString</font> as it stands, forces us to write much unsatisfactory instances:</div><div><br></div><div><div><pre>instance IsString NonEmptyText where</pre><pre>  <span class="n">fromString (c:cs)</span> = NonEmptyText.new c cs</pre><pre>  fromString [] = error "unavoidable runtime panic" -- argh</pre></div></div><div><span class="n"><br></span></div><div>... despite <font face="monospace">fromString</font> being invoked, presumably with OverloadedStrings, on a string literal — which is statically known at compile-time.</div><div><br></div><div>I'd imagine upgrading the interface with GHC's own TypeLits:</div><div><br></div><div><div><pre>class KnownSymbol lit => IsString' str lit where</pre><pre>  fromString' :: proxy lit -> str</pre><pre><br></pre></div></div><div>This 2-parameter formulation of IsString enables pretty straightforward user code, along the lines of:</div><div><br></div><div><font face="monospace">type family MyInvariant (s :: Symbol) :: Constraint where</font></div><div><font face="monospace">  MyInvariant "" = TypeError ('Text "Empty string literal")</font></div><div><font face="monospace">  MyInvariant _s = ()</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">instance (MyInvariant lit, KnownSymbol lit) => IsString' NonEmptyText lit where</font></div><div><font face="monospace">  fromString' = nothingImpossible . NeT.fromText . toText . symbolVal where</font></div><div><font face="monospace">    nothingImpossible (Just x) = x</font></div><div><font face="monospace">    nothingImpossible Nothing  = error "typelevel invariant violated"</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">text1, text2 :: NonEmptyText</font></div><div><font face="monospace">text1 = fromString' $ Proxy @"hello, types"</font></div><div><font face="monospace">text2 = fromString' $ Proxy @""            -- compile error here, as expected!</font></div><div><br></div><div>With the primes, this is possible to write today. With <font face="monospace">AllowAmbiguousTypes</font>, the unwieldy syntax can be abbreviated somewhat, e.g. <font face="monospace">smartLiteral @"overloaded string literal with compiler-checked invariant"</font> — but is still rather awkward... in the same way that ubiquitous T.pack is annoying enough to want to enable OverloadedStrings and stop seeing it all over the place.</div><div><br></div><div>The question is, could this <font face="monospace">IsString'</font> somehow become the <font face="monospace">IsString</font> that <font face="monospace">OverloadedStrings</font> is friends with?</div><div>I guess, not soon, with compatibility in mind... But in principle? Is it even worth trying to pursue the GHC Proposal path?</div><div>Perhaps a new extension <font face="monospace">OverloadedTypelevelStrings</font>?</div><div><br></div><div>Relatedly... Can a GHC Plugin do this?</div><div><br></div><div>And overall, to zoom out of "XY Problem" pitfalls: am I missing a neater way to have "partial" IsString instances, those that can reject some literals at compile-time?</div><div><br></div><div>Having this code work is the goal:</div><div><br></div><div><div><font face="monospace">text1, text2 :: NonEmptyText</font></div><div><font face="monospace">text1 = "hello, types"</font></div><div><font face="monospace">text2 = "" -- compile error</font></div></div><div><br></div><div>Best regards,</div><div>Max</div></div>