<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Can a ghc-plugin do that?<br>
      <br>
      Yes, see
<a class="moz-txt-link-freetext" href="https://hackage.haskell.org/package/overloaded-0.3.1/docs/Overloaded-Symbols.html">https://hackage.haskell.org/package/overloaded-0.3.1/docs/Overloaded-Symbols.html</a><br>
      <br>
      - Oleg<br>
    </p>
    <div class="moz-cite-prefix">On 8.12.2023 17.50, Max Ulidtko wrote:<br>
    </div>
    <blockquote type="cite" cite="mid:YOUC5S.FKWSUGJUCW9T3@gmail.com">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <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">
</span></pre>
          <pre><span class="n">instance IsString Text {- ... -}</span></pre>
          <pre><span class="n">
</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>
</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>
      <br>
      <fieldset class="moz-mime-attachment-header"></fieldset>
      <pre class="moz-quote-pre" wrap="">_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
<a class="moz-txt-link-freetext" href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a>
Only members subscribed via the mailman list are allowed to post.</pre>
    </blockquote>
  </body>
</html>