Stuck on basics

485 views
Skip to first unread message

Blake McBride

unread,
Oct 2, 2015, 1:09:36 AM10/2/15
to CLIPSESG
Playing with CLIPS that I compiled on a 64 bit Linux box.  I can't get utterly basic stuff to work.  For example, I put the following in a file named test.clp:

(assert
  (a 23)
  (b 43)
  (c 27)
  (d 43)
  (e 55))
  

When I do (load "test.clp") I get:

$ ./clips
         CLIPS (6.30 3/17/15)
CLIPS> (load "test.clp")

[CSTRCPSR1] Expected the beginning of a construct.
FALSE


If I put it in by hand, how can I do a simple query on my facts?  For example:


CLIPS> (assert
  (a 23)
  (b 43)
  (c 27)
  (d 43)
  (e 55))
<Fact-5>
CLIPS> (facts)
f-0     (initial-fact)
f-1     (a 23)
f-2     (b 43)
f-3     (c 27)
f-4     (d 43)
f-5     (e 55)
For a total of 6 facts.
CLIPS> (find-all-facts ((c ?x)) TRUE)

[PRNTUTIL2] Syntax Error:  Check appropriate syntax for fact-set query function.
CLIPS> 


I want to find the value associated with "c" (27).  How can I do that?

Thanks.

Blake McBride

CLIPS Support

unread,
Oct 2, 2015, 1:26:31 PM10/2/15
to CLIPSESG
Use the load command to load constructs (defrule, deftemplate, deffacts ...) from a file. Use the batch command to execute functions/commands contained in a file.

CLIPS> (batch test.clp)
TRUE
CLIPS
>
(assert
 
(a 23)
 
(b 43)
 
(c 27)
 
(d 43)
 
(e 55))
<Fact-5>
CLIPS
>

You can also place constructs in a batch file along with commands/functions, but it is better to load them using the load command (since the load command only expects constructs, error handling for a misplaced parenthesis allows the command to more easily move on to the next construct).

There are numerous examples of using the fact query functions in section 12.9.12 of the Basic Programming Guide. It is easier to use these functions with facts with a corresponding deftemplate that has slots, but you can also use them with ordered facts using the "implied" slot.

CLIPS> (find-all-facts ((?f c)) TRUE)
(<Fact-3>)
CLIPS
>
(do-for-all-facts ((?f c)) TRUE
   
(printout t ?f:implied crlf)
   
(printout t (nth$ 1 ?f:implied) crlf))
(27)
27
CLIPS
> (deftemplate f (slot value))
CLIPS
> (assert (f (value 1)))
<Fact-6>
CLIPS
>
(do-for-all-facts ((?f f)) TRUE (printout t ?f:value crlf))
1
CLIPS
>

Blake McBride

unread,
Oct 4, 2015, 8:21:53 AM10/4/15
to CLIPSESG
See below:


On Friday, October 2, 2015 at 12:26:31 PM UTC-5, CLIPS Support wrote:
Use the load command to load constructs (defrule, deftemplate, deffacts ...) from a file. Use the batch command to execute functions/commands contained in a file.

CLIPS> (batch test.clp)
TRUE
CLIPS
>
(assert
 
(a 23)
 
(b 43)
 
(c 27)
 
(d 43)
 
(e 55))
<Fact-5>
CLIPS
>

You can also place constructs in a batch file along with commands/functions, but it is better to load them using the load command (since the load command only expects constructs, error handling for a misplaced parenthesis allows the command to more easily move on to the next construct).


I've been using Lisp since 1980, written a Lisp interpreter, written other languages, etc.  Can't understand why you'd need "batch" for one set of constructs and "load" for others.

More below.

 

There are numerous examples of using the fact query functions in section 12.9.12 of the Basic Programming Guide. It is easier to use these functions with facts with a corresponding deftemplate that has slots, but you can also use them with ordered facts using the "implied" slot.

CLIPS> (find-all-facts ((?f c)) TRUE)
(<Fact-3>)
CLIPS
>
(do-for-all-facts ((?f c)) TRUE
   
(printout t ?f:implied crlf)
   
(printout t (nth$ 1 ?f:implied) crlf))
(27)
27
CLIPS
> (deftemplate f (slot value))
CLIPS
> (assert (f (value 1)))
<Fact-6>
CLIPS
>
(do-for-all-facts ((?f f)) TRUE (printout t ?f:value crlf))
1
CLIPS
>



It seems to me that querying your knowledge database should be the most basic, natural, and easy-to-do thing possible.  Prolog queries are easy, simple, and natural.  IMO, querying a CLIPS DB is unnecessarily convoluted, and unnatural.  Yes, I saw the docs.  That too is unnecessarily convoluted.  For example, what about querying facts that weren't done with templates?

In your example you use "c".  Is that positionally sensitive (like one would think it would be)?  In you fact-set you have (?f c).  I understand that ?f is the variable you wish to bind to, but the syntax makes it look like "c" is in the second position of the facts DB - but not true.  Is it positionally sensitive, I don't know.  Is it required to create templates first in order to use the system with an sense?  I don't know.

Look, I am under the impression that CLISP is a really powerful and nice system.  I am sure it is highly functional as-is.  The problem is that the unnecessary convolutions and complexities are an unnecessary barrier to its use by new developers as well as unnecessary complexity in the maintenance of existing CLIPS applications.

Right off the cuff, I'd say:

1.  Make "load" load any valid CLIPS forms.  Make "batch" a synonym.

2.  Create a simple way to query like:  

(query (c ?))     This would match and return all facts with "c" in the first position

(query (c 27))   This would match and return a specific fact "(c 27)"

(test (c 27))   Return TRUE

(test (c 48))  Return FALSE

(test (c ?))  Return TRUE (some fact matches)


This is just some off-the-cuff ideas.  I'd have to give some thought to things like (query (c)).

I am unlikely to drink the current CLIPS Kool-Aid.  I am, however, interested in developing an easy-to-use programmer API that could exist in-addition to the current API.  But I'd only be interested in doing so if the effort is desired and would, if it made sense, be added to CLIPS.

Thanks.

Blake McBride


CLIPS Support

unread,
Oct 5, 2015, 3:30:14 AM10/5/15
to CLIPSESG
Basically people who dislike LISP don't like CLIPS syntax because it looks like LISP and people who like LISP don't like CLIPS syntax because it doesn't behave like LISP.

CLIPS has LISP-like syntax, but it was never intended to be a LISP-like programming language. It is first and foremost a rule engine. Back in the 80s, most of the state-of-the-art rule engines were written in LISP. The syntax of CLIPS was based on a LISP-based product named ART that had been used to develop several expert systems at NASA/Johnson Space Center. A subset of ART syntax was implemented to make porting ART applications to CLIPS easier, but in retrospect we should have used a more C-like syntax. I'd learned a bit of prolog in college and started programming in C, LISP, Flavors, and ART after graduating, so switching between different syntaxes never bothered me, but many people find LISP syntax to be bizarre. The market voted on the merits of LISP for rule engines and it voted thumbs down. 

CLIPS doesn't support nested lists--multifield values are more like vectors--so semantically it's pretty far removed from LISP in being able to treat programs as data (and vice veras) and is more similar to C/Java in that regard. All of the LISP-like syntax in CLIPS could be replaced with C-like syntax without any loss of functionality. It would be of far more benefit to the majority of CLIPS users who have little to no familiarity with LISP to make CLIPS more C-like than making it more LISP-like.

You don't use batch for one set of constructs and load for another. You typically use batch for executing functions/commands and load for compiling constructs. The key difference is making a function call versus defining the function. Constructs form your CLIPS program and remain resident until they are explicitly deleted. The C++/Java corollaries for constructs would be things like classes and methods. Calling a function/command at the command prompt is transitory. It can have side effects that modify the environment, but the data representing the function call is discarded once it's completed. 

When invoked, the batch command overrides standard input for the read-eval-print loop (REPL). When the REPL requests a character, the batch command will provide one from the file until none remain at which point that batch file will be closed and input will then either be retrieved from the next batch file in the chain or from the user. Because batch files echo their content to standard output, they are undesirable for loading constructs. During development, you're not going to want to see hundreds of rules echoed to the REPL each time you use the batch command to process a file containing constructs.

If you use the load command to process a file containing constructs, you'll get minimal output. A single character will be printed for each construct loaded (or the construct name if you have watch compilations enabled) and you'll get error messages for constructs that can not be processed. Because only constructs are loaded, you have better control over error recovery. For example, suppose the following construct was placed in file named rules.clp:

(defrule hello
   
=>
   
(assert (hello)))
   
(printout t "Hello World" crlf)
   
(assert (goodbye)))


This is the output you'd see from using load command:

CLIPS> (clear)
CLIPS
> (watch compilations)
CLIPS
> (load rules.clp)
Defining defrule: hello +j+j


[CSTRCPSR1] Expected the beginning of a construct.

FALSE
CLIPS
>


The hello rule has an extra parenthesis at the end of the (assert (hello)) command in the actions of the rule. This still creates a valid rule, but the subsequent text that was supposed to be part of the rule is now orphaned. Because the load command only loads constructs it generates a message indicating that it was not able to find a valid construct after the hello defrule. If there were additional constructs in this file, it would keep reading input until it was able to find a valid construct.

If you use the batch command on the same file, you'd get these results:

CLIPS> (clear)
CLIPS
> (batch rules.clp)
TRUE
CLIPS
> (defrule hello
   
=>
   
(assert (hello)))
CLIPS
>    (printout t "Hello World" crlf)
Hello World
CLIPS
>    (assert (goodbye)))
<Fact-1>
CLIPS
>


In addition to the entire contents of the batch file being echoed, the orphaned actions of the rule end up being processed rather than skipped, which is not desirable behavior.

Using batch for scripts and load for constructs isn't all that different from using make to process a make file and javac to compile a java file. Many rule engines don't even have a REPL, so you'd store your rules in files and make API calls to the engine to manipulate the environment. Even with languages that support a REPL and API calls, separating rules and scripts makes sense because you might want to use scripts during development and debugging and API calls for deployment. 

Instance query functions were introduced with the CLIPS Object Oriented Language back in 1991. At the time, the object system was not tightly integrated with the rule language so the query functions provided a mechanism for brute force pattern matching. Ideally for queries, you'd like to be able to express multiple arbitrary patterns so that the query is basically processed like a rule with just a set of conditions and no actions (e.g. (query "(person (age ?age&:(>= ?age 18)))")). I recall ART supporting something like this and I think it was implemented by dynamically creating a rule, grabbing the matching data from it, then deleting it. Collect and accumulation conditional elements are another mechanism supported for retrieving groups of objects in the conditions of a rule, but these are not supported by CLIPS. 

Fact query functions weren't added until 2005 when I had need of some functionality for iterating over groups of facts. It was easier to replicate the functionality of the instance query functions than add the collect/accumulate condition elements to CLIPS.

If you want to just do some simple queries, you can create some methods which iterate over the list of facts looking for specific values.

CLIPS>
(defmethod query ((?relation SYMBOL))
   
(bind ?results (create$))
   
(progn$ (?f (get-fact-list))
     
(if (eq (fact-relation ?f) ?relation)
         
then
         
(bind ?results (create$ ?f ?results))))
   
?results)  
CLIPS
>    
(defmethod query ((?relation SYMBOL) (?value OBJECT))
   
(bind ?results (create$))
   
(progn$ (?f (query ?relation))
     
(if (and (member$ implied (fact-slot-names ?f))
               
(member$ ?value (fact-slot-value ?f implied)))
         
then
         
(bind ?results (create$ ?f ?results))))
   
?results)      
CLIPS
> (assert (a 1) (a 2) (b 2) (b 3))
<Fact-4>
CLIPS
> (query a)
(<Fact-2> <Fact-1>)
CLIPS
> (query b 2)
(<Fact-3>)
CLIPS
>


Ordered facts get little love. They were originally the only data representation in CLIPS, but they had significant drawbacks compared to deftemplate facts which were introduced later. The ordered pattern (auto ? ? ? ?price&:(> ?price 10000)) is a maintenance nightmare compared to (auto (price ?price&:(> ?price 10000))). Imagine adding or removing a piece of data to a fact and then having examine hundreds of rules to determine which patterns were positionally effected by that change. They are useful for creating small examples since you don't have to create deftemplates for them, but for anything else I rarely use them. 

Queries are an integral part of prolog because they initiate computation. They aren't integral for forward chaining systems like CLIPS because they're not needed to initiate computation. You provide the initial data and then just start the engine running. Queries can be useful for debugging retrieving program output, but are certainly not necessary. Decades ago, I learned some backward chaining prolog before I started with forward chaining rule languages and neither seemed inherently better or worse than the other. I think if you're expecting to use a forward chaining system in the same manner as a backward chaining system or vice versa you're going to be disappointed.

Blake McBride

unread,
Oct 13, 2015, 9:48:39 PM10/13/15
to CLIPSESG
Hi,


On Monday, October 5, 2015 at 2:30:14 AM UTC-5, CLIPS Support wrote:
Basically people who dislike LISP don't like CLIPS syntax because it looks like LISP and people who like LISP don't like CLIPS syntax because it doesn't behave like LISP.

Probably some truth to that.
 

CLIPS has LISP-like syntax, but it was never intended to be a LISP-like programming language. It is first and foremost a rule engine. Back in the 80s, most of the state-of-the-art rule engines were written in LISP. The syntax of CLIPS was based on a LISP-based product named ART that had been used to develop several expert systems at NASA/Johnson Space Center. A subset of ART syntax was implemented to make porting ART applications to CLIPS easier, but in retrospect we should have used a more C-like syntax. I'd learned a bit of prolog in college and started programming in C, LISP, Flavors, and ART after graduating, so switching between different syntaxes never bothered me, but many people find LISP syntax to be bizarre. The market voted on the merits of LISP for rule engines and it voted thumbs down. 

CLIPS doesn't support nested lists--multifield values are more like vectors--so semantically it's pretty far removed from LISP in being able to treat programs as data (and vice veras) and is more similar to C/Java in that regard. All of the LISP-like syntax in CLIPS could be replaced with C-like syntax without any loss of functionality. It would be of far more benefit to the majority of CLIPS users who have little to no familiarity with LISP to make CLIPS more C-like than making it more LISP-like.

I love lisp, but perhaps you are right.
From the perspective of someone who has close to as many years experience as you, I respectfully disagree.  I know you've done it this way for a long time, and it seems right, but it is not for two reasons:

1.  It is unnecessarily awkward and confusing to new people looking at CLIPS.

2.  Lisp, Python, Scheme, Ruby, Perl, node,...... all load a file with definitions or global commands of any type.  CLIPS behavior is arbitrary, capricious, and unnecessary.
Look, adding facts is simple and natural in CLIPS.  What would be more natural and necessary than doing the opposite, querying?  Imagine an SQL database that made it easy to add data but you had to write a program if you wanted to query it.  No one would use it!  Once the data is in, the next natural thing is to get it back out (hopefully with additional, forward-chaining facts).  It is especially good for debugging and learning the system!

Let me give an analogy.  Imaging a programming language in which you have to type "end-of-line" at the end of each and every line.  Crazy as hell, isn't it?  When a new programmer sees the language, they think it means just hit your new-line or enter key.  They can't seem to get it to work.  They inquire.  The author tells the, no, no, no.  You have to literally type "end-of-line".  You say that's the craziest thing you ever heard.  They tell you that's the way it's done for some historic reason, and that once you get used to it, you'll hardly notice it.  Nuts!

The way it has always been done, and where it came from are utterly immaterial.  What matters is what makes the system easier to learn and use.  I feel confident that an easy to use query syntax would make CLIPS more accessible, easy to use and learn, and a more appealing system.

On the flip side, I really appreciate your work and contribution.  It is very impressive.

Blake McBride


 
...

Joshua Scoggins

unread,
Oct 14, 2015, 12:15:21 PM10/14/15
to CLIPSESG
CLIPS may not have been meant to be a LISP like language (I do like that you can't nest multifields in multifields) but that didn't stop me from writing my own apply$, filter$, and lambda functions/methods :D (not in C but in CLIPS).

Also, the distinction between load and batch is a nice thing. While we use batch* almost exclusively, I do see the need for the two, especially that you can use batch to simulate input. Really cool feature. The distinction threw me off for a little but when I first started but not anymore.

Blake McBride

unread,
Oct 14, 2015, 8:02:21 PM10/14/15
to CLIPSESG
What can you do with a separate load & batch that couldn't be done with a combined load?

CLIPS Support

unread,
Oct 14, 2015, 8:52:43 PM10/14/15
to CLIPSESG
See my prior response for an example. You can increase the number of relevant error messages you get by using load rather than batch. When everything is delimited by parenthesis, a single misplaced parenthesis can make error recovery problematic. Because the load command is expecting specific keywords, it can skip to the next keyword if an error is encountered. 

I remember the days when compilers often aborted compilation when an error was encountered. That sucked. Compile. Fix error. Compile. Fix next error. Compile. Fix next error.

I can't speak for everyone, but for me having a separate command for loading only constructs makes development easier. 
Reply all
Reply to author
Forward
0 new messages