7 ms·
Once you've got return type polymorphism, you really start to miss it in other languages. The simplest example possible is "mempty" mempty :: a gets the "defa
by moomin 9y ago
Once you've got return type polymorphism, you really start to miss it in other languages. The simplest example possible is "mempty"
mempty :: a
gets the "default" value of a. Which makes no sense in a language where you need an instance to have polymorphism.
(This, incidentally, is also why all OO serialization libraries are awful.)
- unhammer 9y agoOTOH, I feel it's taken too far in the regex packages. They feel extremely daunting for someone just getting started with Haskell. E.g. the wiki says that Text.Regex.TDFA is the thing to use. First try: λ> "abc" =~ "(a|b).*" <interactive>:1558:1-18: Non type-variable argument in the constraint: RegexContext Text.Regex.TDFA.Regex source1 target (Use FlexibleContexts to permit this) When checking that ‘it’ has the inferred type it :: forall source1 target. (Data.String.IsString source1, RegexContext Text.Regex.TDFA.Regex source1 target) => target <interactive>:1558:7-8: Could not deduce (RegexMaker Text.Regex.TDFA.Regex CompOption ExecOption source0) arising from a use of ‘=~’ from the context (Data.String.IsString source1, RegexContext Text.Regex.TDFA.Regex source1 target) bound by the inferred type of it :: (Data.String.IsString source1, RegexContext Text.Regex.TDFA.Regex source1 target) => target at <interactive>:1558:1-18 The type variable ‘source0’ is ambiguous Note: there are several potential instances: instance RegexMaker Text.Regex.TDFA.Regex CompOption ExecOption Data.ByteString.Internal.ByteString -- Defined in ‘Text.Regex.TDFA.ByteString’ instance RegexMaker Text.Regex.TDFA.Regex CompOption ExecOption Data.ByteString.Lazy.Internal.ByteString -- Defined in ‘Text.Regex.TDFA.ByteString.Lazy’ instance RegexMaker Text.Regex.TDFA.Regex CompOption ExecOption (Data.Sequence.Seq Char) -- Defined in ‘Text.Regex.TDFA.Sequence’ ...plus one other In the expression: "abc" =~ "(a|b).*" In an equation for ‘it’: it = "abc" =~ "(a|b).*" <interactive>:1558:10-18: Could not deduce (Data.String.IsString source0) arising from the literal ‘"(a|b).*"’ from the context (Data.String.IsString source1, RegexContext Text.Regex.TDFA.Regex source1 target) bound by the inferred type of it :: (Data.String.IsString source1, RegexContext Text.Regex.TDFA.Regex source1 target) => target at <interactive>:1558:1-18 The type variable ‘source0’ is ambiguous Note: there are several potential instances: instance Data.String.IsString aeson-1.2.1.0:Data.Aeson.Types.Internal.Value -- Defined in ‘aeson-1.2.1.0:Data.Aeson.Types.Internal’ instance Data.String.IsString Data.ByteString.Builder.Internal.Builder -- Defined in ‘Data.ByteString.Builder’ instance Data.String.IsString Data.ByteString.Internal.ByteString -- Defined in ‘Data.ByteString.Internal’ ...plus 9 others In the second argument of ‘(=~)’, namely ‘"(a|b).*"’ In the expression: "abc" =~ "(a|b).*" In an equation for ‘it’: it = "abc" =~ "(a|b).*" Scary. Next most promising hit on ddg is bos' tutorial at http://www.serpentine.com/blog/2007/02/27/a-haskell-regular-expression-tutorial/ http://www.serpentine.com/blog/2007/02/27/a-haskell-regular-... which says I should be able to get a list of results by specifying the context [String]: λ> "abc" =~ "(a|b).*" :: [String] <interactive>:1497:1-5: No instance for (Data.String.IsString source10) arising from the literal ‘"abc"’ The type variable ‘source10’ is ambiguous Note: there are several potential instances: instance Data.String.IsString aeson-1.2.1.0:Data.Aeson.Types.Internal.Value -- Defined in ‘aeson-1.2.1.0:Data.Aeson.Types.Internal’ instance Data.String.IsString Data.ByteString.Builder.Internal.Builder -- Defined in ‘Data.ByteString.Builder’ instance Data.String.IsString Data.ByteString.Internal.ByteString -- Defined in ‘Data.ByteString.Internal’ ...plus 7 others In the first argument of ‘(=~)’, namely ‘"abc"’ In the expression: "abc" =~ "(a|b).*" :: [String] In an equation for ‘it’: it = "abc" =~ "(a|b).*" :: [String] <interactive>:1497:7-8: No instance for (RegexContext Text.Regex.TDFA.Regex source10 [String]) arising from a use of ‘=~’ The type variable ‘source10’ is ambiguous Note: there is a potential instance available: instance RegexLike a b => RegexContext a b [[b]] -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ In the expression: "abc" =~ "(a|b).*" :: [String] In an equation for ‘it’: it = "abc" =~ "(a|b).*" :: [String] <interactive>:1497:10-18: No instance for (Data.String.IsString source0) arising from the literal ‘"(a|b).*"’ The type variable ‘source0’ is ambiguous Note: there are several potential instances: instance Data.String.IsString aeson-1.2.1.0:Data.Aeson.Types.Internal.Value -- Defined in ‘aeson-1.2.1.0:Data.Aeson.Types.Internal’ instance Data.String.IsString Data.ByteString.Builder.Internal.Builder -- Defined in ‘Data.ByteString.Builder’ instance Data.String.IsString Data.ByteString.Internal.ByteString -- Defined in ‘Data.ByteString.Internal’ ...plus 7 others In the second argument of ‘(=~)’, namely ‘"(a|b).*"’ In the expression: "abc" =~ "(a|b).*" :: [String] In an equation for ‘it’: it = "abc" =~ "(a|b).*" :: [String] … so something about overloaded strings is confusing the type checker probably. Since I'm not a complete newbie I know I can specify string types explicitly: λ> ("abc"::String) =~ ("(a|b).*"::String) :: [String] <interactive>:1504:17-18: No instance for (RegexContext Text.Regex.TDFA.Regex String [String]) arising from a use of ‘=~’ In the expression: ("abc" :: String) =~ ("(a|b).*" :: String) :: [String] In an equation for ‘it’: it = ("abc" :: String) =~ ("(a|b).*" :: String) :: [String] That's … shorter, but still daunting. If I'm a bit more experienced, I know that I "just" have to do λ> :i RegexContext class RegexLike regex source => RegexContext regex source target where match :: regex -> source -> target matchM :: Monad m => regex -> source -> m target -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.RegexLike’ instance RegexContext Text.Regex.TDFA.Regex String String -- Defined in ‘Text.Regex.TDFA.String’ instance RegexContext Text.Regex.Regex String String -- Defined in ‘Text.Regex.Posix.String’ instance RegexLike a b => RegexContext a b [[b]] -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b (MatchResult b) -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b Int -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b Bool -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b (AllTextSubmatches [] (b, (MatchOffset, MatchLength))) -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b (AllTextSubmatches [] b) -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b (AllTextMatches [] b) -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b (AllSubmatches [] (MatchOffset, MatchLength)) -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b (AllMatches [] (MatchOffset, MatchLength)) -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b (b, b, b, [b]) -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b (b, b, b) -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b (MatchOffset, MatchLength) -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ instance RegexLike a b => RegexContext a b () -- Defined in ‘regex-base-0.93.2:Text.Regex.Base.Context’ and somewhere in that long list of instances I see that although [String] is not an instance, [[String]] is, so finally I see I can do λ> ("abc"::String) =~ ("(a|b).*"::String) :: [[String]] [ [ "abc" , "a" ] ] Of course, the end result looks nice in the code, but it takes forever to discover the API when you're new to the system (and the docs have next to no examples).
- marcosdumay 9y agoIt is daunting and those errors are very hard to follow even after you have some experience with them. That said, there's a component of "you are using it wrong" on your problem. On the middle of some code, when your data is already in well typed variables, you just write: if text =~ "(a|b).*" then trueVal else falseVal and it just works. It doesn't even matter if text is a String, Text, ByteString, or whatever. Also you just write: putStr . concat $ text =~ "(a|b).* and again, it just works. It does not matter that this is a completely different usage.
- tikhonj 9y agoMost Haskellers I know think that library is too complicated—it's trying too hard to make Haskell look like Perl. I haven't seen it used in serious code and I never use it myself; chances are, if I want to parse anything even remotely complicated, a library like Parsec is a better bet.
- tonyg 9y agoThe trick there is to use an explicitly polymorphic instance, which can "decide what it should have been all along" later on. I've written about the idea: https://eighty-twenty.org/2015/01/25/monads-in-dynamically-typed-languages https://eighty-twenty.org/2015/01/25/monads-in-dynamically-t.... (OO folklore has long had some form of this idea; in limited forms, you see it in Smalltalk images stretching back decades.)
- louthy 9y agoNot sure if I exactly follow, but this is an implementation of Monoid in C#. The interface can be seen as the type-class definition. The structs are the equivalent of class instances. If you look at the `static class Monoid` then you can see a general implementation of mconcat which returns an A and works with Empty and Append. The Program at the end shows it in use with List and String types. public interface Monoid<A> { A Empty(); A Append(A x, A y); } public struct MString : Monoid<string> { public string Append(string x, string y) => x + y; public string Empty() => ""; } public struct MList<A> : Monoid<List<A>> { public List<A> Append(List<A> x, List<A> y) => x.Concat(y).ToList(); public List<A> Empty() => new List<A>(); } public static class List { public static S Fold<S, A>(this IEnumerable<A> ma, S state, Func<S, A, S> f) { foreach(var a in ma) { state = f(state, a); } return state; } public static List<A> New<A>(params A[] items) => new List<A>(items); } public static class Monoid { public static A Concat<MA, A>(IEnumerable<A> ma) where MA : struct, Monoid<A> => ma.Fold(default(MA).Empty(), default(MA).Append); } class Program { static void Main(string[] args) { var strs = new[] { "Hello", ",", " ", "World" }; var lists = new[] { List.New(1, 2, 3), List.New(4, 5, 6) }; var str = Monoid.Concat<MString, string>(strs); var list = Monoid.Concat<MList<int>, List<int>>(lists); } } Obviously it's not as elegant as Haskell, but does this not fit your requirement?
- marcosdumay 9y agoCan you see how that small change on the interface makes it awful to write code that is generic on Monoid? It does not fit the requirements. It makes the kind of code people write in Haskell absolutely not viable.
- louthy 9y agoWhy don't you deal with specifics? I am not talking about its attractiveness, I have already made clear that I think Haskell is more elegant. But, why is this "not viable" or "does not fit the requirements"? I have already shown how a totally generic version of mconcat can be implemented - so how is this not writing "code that is generic on Monoid"?