-
Notifications
You must be signed in to change notification settings - Fork 356
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create logical-rules.md #199
Conversation
Do you think it's possible to create a library for logical programming paradigm? Can you, please, provide some real-life cases when logical programming will be more preferable than functional/oo approach? |
Logical programming cannot be added as a library without huge performance impact (each expression shell he wrapped into a closure instance), and syntax will be ugly in any case. Clean syntax for logic conditions is absolutely required for complex logical code. In my practice I used it (I was writing a compiler with extensions of java) I was really missed this feature in my work on others projects, especially to express some complex business logic. But it's hard to provide a small example, since toy tasks can be solved using if/else, and hard logical tasks are hard to explain. Actually, you can imagine a difference by comparing a yacc/bison output with original grammar description. But yacc/bison are DSLs, while logical programming is a general programming paradigm, sutable for manu tasks. You may see some long examples of code in http://symade.tigris.org project - just checkout and search for 'rule' in code (unfortunately its outdated, compiles only under java 1.6). PS Expression parser code |
Added an example of a simple logical puzzle solution
Hi @mkizub, You should explain why Kotlin should support the logical paradigm instead to develop these component in a dedicated language. Have you considered a Prolog/Clips to bytecode compiler, easily usable by Kotlin code?
Can you link some source? |
@fvasco, thanks for your questions.
It should not :) Kotlin is a language. It has no obligations or wishes.
As you know, those languages already exists. In real life the problem is an interoperability Programmers will not need to export program's data and internal API to external
I wrote in parenthesis what kind of performance impact it will have. Creating a I'm currently trying to come with a simple example for this logical engine. Can you provide your solution using an imaginary library? Don't bother with implementation, |
Hi @mkizub
Without strong motivation this KEEP may not be considered, and personally I don't see it.
Java and Kotlin are different languages and works well togheter.
data class State(val man: Boolean, val wolf: Boolean, val goat: Boolean, val cole: Boolean)
fun State.forbidden(): Boolean =
(wolf != man && wolf == goat) // the wolf eats the goat
|| (goat != man && goat == cole) // the goat eats the cole
val goal = State(true, true, true, true)
fun solve(state: State) = ferry(state).filterNot(State::forbidden)
fun ferry(cs: State) = with(cs) {
listOfNotNull(
// the man can
// cross alone
copy(man = !man),
// ferry the wolf
copy(man = !man, wolf = !wolf).takeIf { man == wolf },
// ferry the goat
copy(man = !man, goat = !goat).takeIf { man == goat },
// ferry the cole
copy(man = !man, cole = !cole).takeIf { man == cole }
)
}
fun main() {
process(setOf(State(false, false, false, false)), ::solve)
.filter { it.last() == goal }.take(20)
.forEach(::println)
}
// library
fun <T : Any> process(initialFacts: Set<T>, infer: (T) -> Iterable<T>) = sequence {
val facts = HashSet<List<T>>()
var agenda = initialFacts.map { listOf(it) }.toSet()
while (agenda.isNotEmpty()) {
yieldAll(agenda)
facts += agenda
agenda = agenda.flatMap { previous -> infer(previous.last()).map { previous + it } }.toSet()
}
} |
Hi @fvasco And as you see, the difference is that logical code makes depth-first traverse of the graph of states, while your solution makes breadth-first traverse. That's because the distinguishing characteristic of logical programming is the possibility to backtrack to previous state, and try another side of decision fork. And depth-first lookup is natural for logical programming. Definitely none of approaches is better for all situations. The motivation for adding logical paradigm to Kotlin is the same as motivation of adding elements of functional or object-oriented programming. It allows to solve many programming tasks in a cleaner and safer way, compared to imperative or functional approaches.
Well, Kotlin is the same language for all JVM/JS/Native platforms, but they don't work together well. Language/platform compatibility is a very subtle subject. Otherwise, why they added closures and other functional features to Java, if that many functional languages for JVM exists? Why not use Eta (Haskell for JVM) or Kawa (Scheme for JVM), right? |
Hi @mkizub
It answers to your direct question. It shows that your example does not prove that this KEEP helps in Kotlin development.
No, it does not.
Are you joking? I think you should start from the How to propose paragraph. |
Hi @fvasco Proposed logical engine is a tool for programmers. It has many useful features, for example You say, that one can use sequences, co-routines, functional programming etc. I cannot convince you in the same way. I was using this logical engine, and found it to be very useful. That's all I can say. It does not convince you? That's fine. I have no idea about your background, maybe you are like a watchmaker, never had a need of ax. I asked you whether you can propose an example task. A one that cannot be easily solved in functional or imperative paradigm. If you don't have one, than you, probably, will not need anything beyond these paradigms. I argue that adding this logical engine to Kotlin is a simple task. I did it withing last 2 weeks, and 90% of time was spent to learn internals of the compiler. It can be production-ready in a month, if someone will sit near me and answer my questions about the compiler and idea plugin. I argue that this logical engine is compatible with Kolin. Because very little changes are needed for it's syntax and nothing in semantic. Rules are just functions that return iterators. Period. And for this scanty price Kotlin will get support of the logical paradigm. Fully integrated. Being the first widely used language that has support of all imperative, objective, functional and logical paradigms (well, maybe second after Lisp, but Lisp can everything by definition :) ). |
Hi @mkizub, I like logical programming and I think that your idea may improve the language. Honestly, I don't think that an answer like «It should not :) Kotlin is a language. It has no obligations or wishes.» is a good step in the right direction. Your proposed puzzle is a good idea, unfortunately your solution, compared to a pure Kotlin one, requires the trick: !seq.contains(-ns), // prevent infinit loop
// add new state, and remove it on backtracking
seq.push(-ns) ?< seq.pop() Maybe you should evaluate this suggestion to evolve this KEEP and/or to search a better example. Finally I want to highlight you that some slogan like «it can be used as a compact, clean and efficient replacement for all those chained filter/map/etc; and so on.» are really interesting, but you should show how much it is compact, you should explain why it is cleaner and you should prove how much it is efficient. Unfortunately these advantages are not evident to me. Without data, facts and good example, this KEEP does not look valuable to me. But this is only a personal opinion, and my value is zero in this repository. Good luck! |
Hi @mkizub
Please avoid direct mail, it is hard to track and inaccessible to other contributors, you can find really good suggestions in the -already mentioned- "How to propose" section.
I solved the quiz using valid Kotlin syntax, I don't see any reason to design a different syntax to solve an already solved task.
I fixed the example, I have to consider that the difficulty is really subjective.
I agree, however the "trick" is an implementation detail.
This is the Occam Preferred Solution™ and it looks really reasonable to me. Please avoid to submit to me further requests, I already expressed my -personal- point of view and each point of this discussion does not carry any valuable thought. |
Hello @fvasco Sorry for the direct e-mail, I had no intention to be intrusive. But I explained my reasons to send it, and they are still working, that's why I will not make a detailed response to your answer. |
Personally, I would prefer a DSL syntax within regular Kotlin over a new "logic" syntax (expressiveness over conciseness). I believe this does not have to be ugly. Concerning your example: |
@quickstep24 , thanks for the found bug in the sample puzzle, I fixed it. Of cause, it should be like
As for the library/DSL. It took me about 4 month to invent such a logical engine. Believe me or not, I started from an attempt to write a library. Well, actually I started with attempts to use existing Prolog interpreters (libraries) for JVM. But It was impossible to export whole program's data to those libraries, so I evolved to an attempt to write a library, where methods were implementing all those AND/OR/CUT/etc logical primitives. But this effectively means that every expression in logical code will be a lambda wrapped into a closure. A separate inner class for each expression. Imagine your program, it's size and performance, if every statement/expression in it will be compiled into a separate inner class.
No. Sorry, the idea of wrapping every expression into an inner class made such a lasting impression, that I not even tried this approach. I think, the performance will not be significantly down - those closures (for each expression) will be created on method entry, and after that an execution of each expression will be wrapped into a call. Maybe 2 or 3 times slower?.. Not a big deal. What is more important is the syntax and semantic. I tried to invent a good syntax many times, but not found a good one. Words and and or are used too often in the rest of code, to reserve them for rules. Unneeded {..} around each expression. Hard to indent code. And so on. As for semantic, all those AND/OR/CUT/IF-THEN/WHILE/WHEN rules are flow controls, not just expressions. Flow controls may be analyzed and optimized. For example, code from the puzzle
shell be optimized into one boolean expression
during code optimization. Also, the compiler may track bound and unbound states of logical variables. Actually, it's very like tracking for nullable values. Because unbound logical variable has no value, it's null actually. Otherwise we will get all those problems that nullable values were causing without accurate flow control analyze. Maybe, with new Kotlin's conditions engine it will be possible in future, but now it's too limited. |
A proposition to add support of logical programming paradigm to Kotlin.
Initial implementation is in my fork of kotlin's compiler